diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..0a2df9277 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: ['https://www.paypal.com/paypalme/algr453'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/README.md b/README.md index 02384b033..b90ef2e5f 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,8 @@ Who said OAuth/OAuth2 was difficult? Configuring ScribeJava is __so easy your grandma can do it__! check it out: ```java -OAuthService service = new ServiceBuilder(YOUR_API_KEY) - .apiSecret(YOUR_API_SECRET) +OAuthService service = new ServiceBuilder(YOUR_CLIENT_ID) + .apiSecret(YOUR_CLIENT_SECRET) .build(LinkedInApi20.instance()); ``` @@ -77,6 +77,7 @@ ScribeJava support out-of-box several HTTP clients: * HeadHunter ХэдХантер (https://hh.ru/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/HHExample.java) * HiOrg-Server (https://www.hiorg-server.de/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/HiOrgServerExample.java) * Imgur (http://imgur.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/ImgurExample.java) +* Instagram (https://www.instagram.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/InstagramExample.java) * Kaixin 开心网 (http://www.kaixin001.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Kaixin20Example.java) * Kakao (https://kakao.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/KakaoExample.java) * Keycloak (https://www.keycloak.org/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/KeycloakExample.java) @@ -97,6 +98,7 @@ ScribeJava support out-of-box several HTTP clients: * Salesforce (https://www.salesforce.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SalesforceExample.java), [example with Async Ning HTTP Client](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SalesforceNingAsyncExample.java) * Sina (http://www.sina.com.cn/ http://weibo.com/login.php) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SinaWeibo2Example.java), [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SinaWeiboExample.java) * Skyrock (http://skyrock.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SkyrockExample.java) +* Slack (https://slack.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SlackExample.java) * StackExchange (http://stackexchange.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/StackExchangeExample.java) * The Things Network (v1-staging and v2-preview) (https://www.thethingsnetwork.org/) [example v1](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TheThingsNetworkV1StagingExample.java), [example v2 preview](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TheThingsNetworkV2PreviewExample.java) * Trello (https://trello.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TrelloExample.java) @@ -135,7 +137,7 @@ You can pull ScribeJava from the central maven repository, just add these to you com.github.scribejava scribejava-apis - 8.0.0 + 8.3.3 ``` @@ -144,7 +146,7 @@ And in case you need just core classes (that's it, without any external API (FB, com.github.scribejava scribejava-core - 8.0.0 + 8.3.3 ``` @@ -156,7 +158,10 @@ First of all, Pull Requests are welcome, the second option is [donations](https: When you will send the pull request. That's the way for a majority of changes here. Or you can ask someone to make the paid job for you. -In some cases, when I'm interested in changes (technicaly or financialey), I can implement the request myself. +In some cases, when I'm interested in changes (technically or financially), I can implement the request myself. + +## Paid consulting +If you or your business depends on the Scribejava and you need any specific improvement or new feature not currently implemented in the Scribejava, consider contacting me about a paid job. ## Getting started in less than 2 minutes diff --git a/changelog b/changelog index f46c83239..e962e073b 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,33 @@ +[8.3.3] + * update dependencies, including security updates in libraries + +[8.3.2] + * minor fixes and enhances + * update dependencies + * while using async HTTP client, you could miss some Throwables, now they will be thrown + +[8.3.1] + * fix java.lang.NoClassDefFoundError for non-java8 runtimes (e.g. Android 7.1.1) + (thanks to https://github.com/ChristopherGittner) + +[8.3.0] + * add Instagram (https://www.instagram.com/) API (thanks to https://github.com/faent) + * add getErrorMessage method to FacebookAccessTokenErrorResponse. Should be used instead of general getMessage method + from the parent Exception + +[8.2.0] + * add ScopeBuilder to easily specify multiple scopes while requesting OAuth2.0 Access Tokens + * make Base64 en/de-coding not dependent from java8 implementation (use three optional implementation + (internal java 8+, Apache Commons Codec, JAXB) detected in runtime) (thanks to https://github.com/CodingFabian) + * implement possibility to add extra parameters to Access Token Request + (AccessTokenRequestParams#*ExtraParameters methods), https://github.com/scribejava/scribejava/issues/980 + (thanks to https://github.com/pmorch) + +[8.1.0] + * add raw Response (with HTTP response code and body) as member to the OAuth2AccessTokenErrorResponse + * add possibility to set "" (empty string) as apiSecret + * add Slack API (https://slack.com/) (thanks to https://github.com/petrkopotev) + [8.0.0] * add Kakao API (https://kakao.com/) (thanks to https://github.com/v0o0v) * support chunks in JDKHttpClient's Multipart (thanks to https://github.com/eos1d3) @@ -53,7 +83,8 @@ [6.4.1] * support TLS 1.3 in JDK 11 for Salesforce - * fix NPE in Apache HTTP client in case of empty body in HTTP response (e.g. with 204 response code) (thanks to https://github.com/SainagNeelamPatnaik) + * fix NPE in Apache HTTP client in case of empty body in HTTP response (e.g. with 204 response code) + (thanks to https://github.com/SainagNeelamPatnaik) * separate OAuth1.0a and OAuth2.0 classes [6.3.0] @@ -65,7 +96,8 @@ and it should be set per request, not per created OAuthService [6.2.0] - * add new API Microsoft Azure Active Directory (Azure AD) 2.0 (thanks to https://github.com/rzukow and https://github.com/dgrudenic) + * add new API Microsoft Azure Active Directory (Azure AD) 2.0 + (thanks to https://github.com/rzukow and https://github.com/dgrudenic) [6.1.0] * add new API Keycloak (https://www.keycloak.org/) (thanks to https://github.com/JureZelic) @@ -73,18 +105,22 @@ [6.0.0] * make redirect_uri optional while Access Token requesting on OAuth 2.0 (thanks to https://github.com/computerlove) - * switch to java 9+ (from java 7 only) for compilation. Runtime is still java 7+. Complement README with links and RFC descriptions. + * switch to java 9+ (from java 7 only) for compilation. Runtime is still java 7+. + Complement README with links and RFC descriptions. * switch OAuth2 Bearer Token Usage from enum OAuth2SignatureType to interface BearerSignature to be extensible * add new API Wunderlist (https://www.wunderlist.com/) (thanks to https://github.com/M-F-K) [5.6.0] - * remove support for obsolete NetEase (http://www.163.com/) and sohu 搜狐 (http://www.sohu.com/) (thanks to https://github.com/zawn) + * remove support for obsolete NetEase (http://www.163.com/) and sohu 搜狐 (http://www.sohu.com/) + (thanks to https://github.com/zawn) * add Multipart functionality to JDK Http Client (thanks to https://github.com/eos1d3) * switch OAuth2 ClientAuthenticationType from enum to interface ClientAuthentication to be extensible according to https://tools.ietf.org/html/rfc6749#section-2.3.2 (thanks to https://github.com/zawn) - * add RuntimeException processing in async http clients (delivered to onError callbacks) (thanks to https://github.com/jochen314) + * add RuntimeException processing in async http clients (delivered to onError callbacks) + (thanks to https://github.com/jochen314) * check 200 status code from response in OAuth2AccessTokenExtractor (thanks to https://github.com/jochen314) - * fix case sensitive Http Headers comparison and sending Content-Type header along with content-type (thanks to https://github.com/marnix) + * fix case sensitive Http Headers comparison and sending Content-Type header along with content-type + (thanks to https://github.com/marnix) * add HiOrg-Server (https://www.hiorg-server.de/) API (thanks to https://github.com/MartinBoehmer) [5.5.0] @@ -97,7 +133,8 @@ * fix missing support for scope for refresh_token grant_type (thanks to https://github.com/tlxtellef) * add email field to VKOAuth2AccessToken (thanks to https://github.com/grouzen) * add new API - Automatic (https://www.automatic.com/) (thanks to https://github.com/ramsrib) - * add new API - Fitbit (https://www.fitbit.com/) (thanks to https://github.com/JustinLawler and https://github.com/alexthered) + * add new API - Fitbit (https://www.fitbit.com/) + (thanks to https://github.com/JustinLawler and https://github.com/alexthered) * deprecate OAuthConfig * OAuth1.0: send "oob" instead of null callback while requesting RequestToken (thanks to https://github.com/Rafaelsk) @@ -111,7 +148,8 @@ * add required param version to VK ВКонтакте (http://vk.com/) urls [5.2.0-java7again] - * allow 'null' as callback. It's an optional parameter. Remove "oob" as default (thanks to https://github.com/massongit) + * allow 'null' as callback. It's an optional parameter. Remove "oob" as default + (thanks to https://github.com/massongit) * java7 compatible again! [5.1.0] @@ -124,17 +162,20 @@ * drop Java 7 backward compatibility support, become Java 8 only (was reverted in v5.2.0-java7again) * add JSON token extractor for OAuth 1.0a (thanks to https://github.com/evstropovv) * add new API - uCoz (https://www.ucoz.com/) (thanks to https://github.com/evstropovv) - * add PKCE (RFC 7636) support (Proof Key for Code Exchange by OAuth Public Clients) (thanks for suggesting to https://github.com/dieseldjango) + * add PKCE (RFC 7636) support (Proof Key for Code Exchange by OAuth Public Clients) + (thanks for suggesting to https://github.com/dieseldjango) * switch to use HTTP Basic Authorization by default in requests with need of (2.3. Client Authentication) https://tools.ietf.org/html/rfc6749#section-2.3 Can be overrided in API class * add support for client_credentials grant type (thanks to https://github.com/vivin) * add support for RFC 7009 OAuth 2.0 Token Revocation (thanks to https://github.com/vivin) * add OAuth2Service signRequest method accepting just String, not OAuth2 Access Token Object. Remove signRequest from abstract OAuthService. 2.0 and 1.0a will be a bit more different now. - * drop toString method from *Tokens to prevent leak of sensible data (token ans secrets) (thanks to https://github.com/rcaa) + * drop toString method from *Tokens to prevent leak of sensible data (token ans secrets) + (thanks to https://github.com/rcaa) * add Apache HttpComponents HttpClient support in separate module (thanks to https://github.com/sschwieb) * add support for appsecret_proof in Facebook - * update Facebook v2.8 -> v2.11 (version can be configured while constructing OAuthService - use FacebookApi.customVersion("2.11")) + * update Facebook v2.8 -> v2.11 + (version can be configured while constructing OAuthService - use FacebookApi.customVersion("2.11")) [4.2.0] * DELETE in JdkClient permits, but not requires payload (thanks to https://github.com/miguelD73) @@ -147,21 +188,27 @@ * update Live API (thanks to https://github.com/typhoon17) [4.1.1] - * omit the client_secret parameter if it is an empty string while refreshing token (thanks to https://github.com/KungfuPancake) + * omit the client_secret parameter if it is an empty string while refreshing token + (thanks to https://github.com/KungfuPancake) * allow perms to be specified in Flickr Api (read, write, or delete) (thanks to https://github.com/rogerhu) - * OdnoklassnikiService should consider params in a body while signing the request (thanks to https://github.com/MrNeuronix) + * OdnoklassnikiService should consider params in a body while signing the request + (thanks to https://github.com/MrNeuronix) * do not open OutputStream for output while sending empty body in HTTP requests in the default JDK Http client [4.1.0] - * make client_secret optional in OAuth2 while requesting AccessToken (if set to null, it's not required by OAuth2 specs) + * make client_secret optional in OAuth2 while requesting AccessToken + (if set to null, it's not required by OAuth2 specs) * move OAuth1 SignatureType from ServiceBuilder to API * add body for PATCH HTTP method - * make addOAuthParams appendSignature methods protected in OAuth10aService (to override them in case of need) (thanks to https://github.com/vivin) + * make addOAuthParams appendSignature methods protected in OAuth10aService (to override them in case of need) + (thanks to https://github.com/vivin) [4.0.0] - * Remove OAuthRequestAsync, just OAuthRequest. Request should know about sync vs async. Move default Http engine to JDKHttpClient. + * Remove OAuthRequestAsync, just OAuthRequest. Request should know about sync vs async. + Move default Http engine to JDKHttpClient. * introduce SignatureType for OAuth2.0 to implement Bearer signing for the requests - * switch Google, GitHub, Facebook OAuth2.0 oauth requests signing to more secured recommended variant (GET-param -> header Bearer) + * switch Google, GitHub, Facebook OAuth2.0 oauth requests signing to more secured recommended variant + (GET-param -> header Bearer) * introduce custom nonstandard Facebook AccessTokenErrorResponse [3.4.1] @@ -174,14 +221,16 @@ * add support for byte[] and File (async only) payload in OAuth Requests (thanks to https://github.com/keijohyttinen) * add support for HTTP verbs (thanks to https://github.com/keijohyttinen) * add OkHttp http client support (thanks to https://github.com/arcao) - * add default HTTP client configs (to use like 'new ServiceBuilder().httpClientConfig(OkHttpHttpClientConfig.defaultConfig())') + * add default HTTP client configs + (to use like 'new ServiceBuilder().httpClientConfig(OkHttpHttpClientConfig.defaultConfig())') * you can use your own impl of AsyncHttpClient [3.3.0] * update Facebook v2.6 -> v2.8 * add The Things Network API (v1-staging and v2-preview) (thanks to https://github.com/jpmeijers) * add Box (thanks to https://github.com/MclaughlinSteve) - * fix: OAuth20Service::refreshAccessToken should use RefreshTokenEndpoint, not AccessTokenEndpoint (thanks to https://github.com/vivin) + * fix: OAuth20Service::refreshAccessToken should use RefreshTokenEndpoint, not AccessTokenEndpoint + (thanks to https://github.com/vivin) * move signRequest method to OAuthService (common for OAuth1 and OAuth2) (thanks to https://github.com/apomelov) * drop deprecated setConnectionKeepAlive method @@ -190,14 +239,17 @@ * handle OAuth2 error response for Issuing an Access Token (thanks to juherr) [3.1.0] - * fix OdnoklassnikiServiceImpl signature, params for hash must be sorted in lexicographic order, see http://new.apiok.ru/dev/methods/ + * fix OdnoklassnikiServiceImpl signature, params for hash must be sorted in lexicographic order, + see http://new.apiok.ru/dev/methods/ * add posibility to use externally created http client * make ScribeJava compilable under jdk7 (checkstyle downgraded for jdk 1.7) * add travis CI (check [oracle|open]jdk7 oraclejdk8) [3.0.0] - * create abstract HTTP Client layer to support different HTTP clients as plugins (AHC and Ning support becames maven submodules) - * remove changing global JVM property http.keepAlive, deprecate controlling this property inside of ScribeJava (thanks to wldaunfr and rockihack) + * create abstract HTTP Client layer to support different HTTP clients as plugins + (AHC and Ning support becames maven submodules) + * remove changing global JVM property http.keepAlive, deprecate controlling this property inside of ScribeJava + (thanks to wldaunfr and rockihack) [2.8.1] * add Salesforce sandbox API support @@ -238,8 +290,10 @@ [2.5.2] * add Google Async Exmaple (with bugfix for it to work) * add OSGI manifest metadata - * apiSecret is not mandatory parameter in config (to use on client sides and other flows without need of the API secret) - * implement OAuth2 Authorization Response parsing in the OAuth20Service (to extract code and state from url, useful for Android) + * apiSecret is not mandatory parameter in config + (to use on client sides and other flows without need of the API secret) + * implement OAuth2 Authorization Response parsing in the OAuth20Service + (to extract code and state from url, useful for Android) * update ok.ru API urls, add 'state' support, add refresh token to the example [2.4.0] diff --git a/checkstyle.xml b/checkstyle.xml index c9b0fac9b..f7efbad65 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -96,7 +96,9 @@ - + diff --git a/donate.md b/donate.md index ee6a1a6e7..99d3686dc 100644 --- a/donate.md +++ b/donate.md @@ -1,12 +1,10 @@ You can now help ScribeJava not only by Pull Requests. -You can use [https://paypal.me/kullfar](https://paypal.me/kullfar) directly - -or try donation button from PayPal: [![Donate with PayPal button](https://www.paypalobjects.com/en_US/RU/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=E3XAUM2ET2R3Y&source=url) +You can use [https://www.paypal.com/paypalme/algr453](https://www.paypal.com/paypalme/algr453) directly. Thanks in advance! -ps.If you can't for any reason use above methods, let me know, we will find the way out. +ps.If you can't for any reason use above method, let me know, we will find the way out. Hall of fame "Donors" (in alphabetical order, if you don't want to be here, just put a note along with the donation):
1.Douglas Ross from USA
diff --git a/pmd.xml b/pmd.xml index 7b5314710..5d93d340b 100644 --- a/pmd.xml +++ b/pmd.xml @@ -17,7 +17,6 @@ - @@ -34,8 +33,6 @@ - - @@ -48,6 +45,7 @@ + @@ -91,7 +89,6 @@ - diff --git a/pom.xml b/pom.xml index a695b50c0..b484b6a71 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ com.github.scribejava scribejava pom - 8.0.0 + 8.3.4-SNAPSHOT ScribeJava OAuth Library The best OAuth library out there https://github.com/scribejava/scribejava @@ -16,6 +16,7 @@ scribejava-core + scribejava-java8 scribejava-apis scribejava-httpclient-ahc scribejava-httpclient-ning @@ -32,10 +33,10 @@ - scm:git:git://github.com/scribejava/scribejava.git - scm:git:git@github.com:scribejava/scribejava.git + scm:git:https://github.com/scribejava/scribejava + scm:git:https://github.com/scribejava/scribejava https://github.com/scribejava/scribejava - scribejava-8.0.0 + HEAD @@ -54,18 +55,18 @@ com.fasterxml.jackson.core jackson-databind - 2.11.3 + 2.14.0 junit junit - 4.13.1 + 4.13.2 test com.squareup.okhttp3 mockwebserver - 4.9.0 + 4.10.0 test @@ -75,7 +76,7 @@ org.apache.felix maven-bundle-plugin - 5.1.1 + 5.1.8 bundle-manifest @@ -89,7 +90,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.2.0 + 3.3.0 ${project.build.outputDirectory}/META-INF/MANIFEST.MF @@ -99,12 +100,12 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.1.1 + 3.2.0 com.puppycrawl.tools checkstyle - 8.37 + 10.4 @@ -124,19 +125,19 @@ org.apache.maven.plugins maven-clean-plugin - 3.1.0 + 3.2.0 org.apache.maven.plugins maven-install-plugin - 2.5.2 + 3.0.1 maven-compiler-plugin - 3.8.1 + 3.10.1 UTF-8 ${java.release} @@ -148,7 +149,7 @@ maven-deploy-plugin - 2.8.2 + 3.0.0 default-deploy @@ -162,7 +163,7 @@ org.apache.maven.plugins maven-resources-plugin - 3.2.0 + 3.3.0 UTF-8 @@ -183,11 +184,12 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.2.0 + 3.4.1 ${java.home}/bin/javadoc UTF-8 -html5 + all,-missing @@ -206,7 +208,6 @@ validate validate - main/java/com/github/scribejava/core/java8/* ${basedir}/src @@ -223,7 +224,7 @@ org.apache.maven.plugins maven-pmd-plugin - 3.13.0 + 3.19.0 net.sourceforge.pmd @@ -269,7 +270,7 @@ 7 - 6.29.0 + 6.51.0 @@ -286,7 +287,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.6 + 3.0.1 sign-artifacts diff --git a/scribejava-apis/pom.xml b/scribejava-apis/pom.xml index 2acf02fb0..4e51bb8b5 100644 --- a/scribejava-apis/pom.xml +++ b/scribejava-apis/pom.xml @@ -5,7 +5,7 @@ com.github.scribejava scribejava - 8.0.0 + 8.3.4-SNAPSHOT ../pom.xml @@ -50,6 +50,12 @@ ${project.version} test + + io.netty + netty-resolver + 4.1.84.Final + test + diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/FacebookApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/FacebookApi.java index 9c3ee7a46..d688248d6 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/FacebookApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/FacebookApi.java @@ -13,9 +13,6 @@ import com.github.scribejava.core.oauth2.clientauthentication.ClientAuthentication; import com.github.scribejava.core.oauth2.clientauthentication.RequestBodyAuthenticationScheme; -/** - * Facebook API - */ public class FacebookApi extends DefaultApi20 { private final String version; diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/InstagramApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/InstagramApi.java new file mode 100644 index 000000000..85e408113 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/InstagramApi.java @@ -0,0 +1,59 @@ +package com.github.scribejava.apis; + +import java.io.OutputStream; +import com.github.scribejava.apis.instagram.InstagramAccessTokenJsonExtractor; +import com.github.scribejava.apis.instagram.InstagramService; +import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.extractors.TokenExtractor; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; +import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.oauth2.clientauthentication.ClientAuthentication; +import com.github.scribejava.core.oauth2.clientauthentication.RequestBodyAuthenticationScheme; + +public class InstagramApi extends DefaultApi20 { + + public static final String LONG_LIVED_ACCESS_TOKEN_ENDPOINT = "https://graph.instagram.com/access_token"; + + private static class InstanceHolder { + + private static final InstagramApi INSTANCE = new InstagramApi(); + } + + public static InstagramApi instance() { + return InstanceHolder.INSTANCE; + } + + @Override + public String getAccessTokenEndpoint() { + return "https://api.instagram.com/oauth/access_token"; + } + + @Override + public String getRefreshTokenEndpoint() { + return "https://graph.instagram.com/refresh_access_token"; + } + + @Override + protected String getAuthorizationBaseUrl() { + return "https://api.instagram.com/oauth/authorize"; + } + + @Override + public TokenExtractor getAccessTokenExtractor() { + return InstagramAccessTokenJsonExtractor.instance(); + } + + @Override + public ClientAuthentication getClientAuthentication() { + return RequestBodyAuthenticationScheme.instance(); + } + + @Override + public InstagramService createService(String apiKey, String apiSecret, String callback, String defaultScope, + String responseType, OutputStream debugStream, String userAgent, HttpClientConfig httpClientConfig, + HttpClient httpClient) { + return new InstagramService(this, apiKey, apiSecret, callback, defaultScope, responseType, debugStream, + userAgent, httpClientConfig, httpClient); + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/MediaWikiApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/MediaWikiApi.java index d3c078f9b..3bb18001d 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/MediaWikiApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/MediaWikiApi.java @@ -36,7 +36,7 @@ public MediaWikiApi(String indexUrl, String niceUrlBase) { } /** - * The instance for wikis hosted by the Wikimedia Foundation.Consumers are requested on + * The instance for wikis hosted by the Wikimedia Foundation. Consumers are requested on * * Special:OAuthConsumerRegistration/propose * . diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/SlackApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/SlackApi.java new file mode 100644 index 000000000..4d834fff1 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/SlackApi.java @@ -0,0 +1,37 @@ +package com.github.scribejava.apis; + +import com.github.scribejava.apis.slack.SlackJsonTokenExtractor; +import com.github.scribejava.core.builder.api.DefaultApi20; + +/** + * Slack.com API + */ +public class SlackApi extends DefaultApi20 { + + protected SlackApi() { + } + + private static class InstanceHolder { + + private static final SlackApi INSTANCE = new SlackApi(); + } + + public static SlackApi instance() { + return SlackApi.InstanceHolder.INSTANCE; + } + + @Override + public String getAccessTokenEndpoint() { + return "https://slack.com/api/oauth.v2.access"; + } + + @Override + protected String getAuthorizationBaseUrl() { + return "https://slack.com/oauth/v2/authorize"; + } + + @Override + public SlackJsonTokenExtractor getAccessTokenExtractor() { + return SlackJsonTokenExtractor.instance(); + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/facebook/FacebookAccessTokenErrorResponse.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/facebook/FacebookAccessTokenErrorResponse.java index b792ad636..ea5053931 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/facebook/FacebookAccessTokenErrorResponse.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/facebook/FacebookAccessTokenErrorResponse.java @@ -1,6 +1,8 @@ package com.github.scribejava.apis.facebook; -import com.github.scribejava.core.exceptions.OAuthException; +import com.github.scribejava.core.model.OAuthResponseException; +import com.github.scribejava.core.model.Response; +import java.io.IOException; import java.util.Objects; /** @@ -14,22 +16,27 @@ * '{"error":{"message":"Error validating application. Invalid application * ID.","type":"OAuthException","code":101,"fbtrace_id":"CvDR+X4WWIx"}}' */ -public class FacebookAccessTokenErrorResponse extends OAuthException { +public class FacebookAccessTokenErrorResponse extends OAuthResponseException { private static final long serialVersionUID = -1277129766099856895L; + private final String errorMessage; private final String type; private final int codeInt; private final String fbtraceId; - private final String rawResponse; - public FacebookAccessTokenErrorResponse(String message, String type, int code, String fbtraceId, - String rawResponse) { - super(message); + public FacebookAccessTokenErrorResponse(String errorMessage, String type, int code, String fbtraceId, + Response response) + throws IOException { + super(response); + this.errorMessage = errorMessage; this.type = type; this.codeInt = code; this.fbtraceId = fbtraceId; - this.rawResponse = rawResponse; + } + + public String getErrorMessage() { + return errorMessage; } public String getType() { @@ -44,15 +51,10 @@ public String getFbtraceId() { return fbtraceId; } - public String getRawResponse() { - return rawResponse; - } - @Override public int hashCode() { - int hash = 5; - hash = 83 * hash + Objects.hashCode(rawResponse); - hash = 83 * hash + Objects.hashCode(getMessage()); + int hash = super.hashCode(); + hash = 83 * hash + Objects.hashCode(errorMessage); hash = 83 * hash + Objects.hashCode(type); hash = 83 * hash + Objects.hashCode(codeInt); hash = 83 * hash + Objects.hashCode(fbtraceId); @@ -70,11 +72,13 @@ public boolean equals(Object obj) { if (getClass() != obj.getClass()) { return false; } - final FacebookAccessTokenErrorResponse other = (FacebookAccessTokenErrorResponse) obj; - if (!Objects.equals(rawResponse, other.getRawResponse())) { + if (!super.equals(obj)) { return false; } - if (!Objects.equals(getMessage(), other.getMessage())) { + + final FacebookAccessTokenErrorResponse other = (FacebookAccessTokenErrorResponse) obj; + + if (!Objects.equals(errorMessage, other.getErrorMessage())) { return false; } if (!Objects.equals(type, other.getType())) { @@ -89,7 +93,7 @@ public boolean equals(Object obj) { @Override public String toString() { return "FacebookAccessTokenErrorResponse{'type'='" + type + "', 'codeInt'='" + codeInt - + "', 'fbtraceId'='" + fbtraceId + "', 'rawResponse'='" + rawResponse - + "', 'message'='" + getMessage() + "'}"; + + "', 'fbtraceId'='" + fbtraceId + "', 'response'='" + getResponse() + + "', 'errorMessage'='" + errorMessage + "'}"; } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/facebook/FacebookAccessTokenJsonExtractor.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/facebook/FacebookAccessTokenJsonExtractor.java index 7171c9fbd..f51935436 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/facebook/FacebookAccessTokenJsonExtractor.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/facebook/FacebookAccessTokenJsonExtractor.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor; +import com.github.scribejava.core.model.Response; import java.io.IOException; /** @@ -29,13 +30,17 @@ public static FacebookAccessTokenJsonExtractor instance() { * * '{"error":{"message":"Error validating application. Invalid application * ID.","type":"OAuthException","code":101,"fbtrace_id":"CvDR+X4WWIx"}}' + * + * @param response response */ @Override - public void generateError(String rawResponse) throws IOException { - final JsonNode errorNode = OAuth2AccessTokenJsonExtractor.OBJECT_MAPPER.readTree(rawResponse).get("error"); + public void generateError(Response response) throws IOException { + final JsonNode errorNode = OAuth2AccessTokenJsonExtractor.OBJECT_MAPPER + .readTree(response.getBody()) + .get("error"); throw new FacebookAccessTokenErrorResponse(errorNode.get("message").asText(), errorNode.get("type").asText(), - errorNode.get("code").asInt(), errorNode.get("fbtrace_id").asText(), rawResponse); + errorNode.get("code").asInt(), errorNode.get("fbtrace_id").asText(), response); } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/fitbit/FitBitJsonTokenExtractor.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/fitbit/FitBitJsonTokenExtractor.java index 3ec56f5be..24ed6025a 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/fitbit/FitBitJsonTokenExtractor.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/fitbit/FitBitJsonTokenExtractor.java @@ -1,8 +1,10 @@ package com.github.scribejava.apis.fitbit; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor; import com.github.scribejava.core.model.OAuth2AccessTokenErrorResponse; +import com.github.scribejava.core.model.Response; import com.github.scribejava.core.oauth2.OAuth2Error; import java.io.IOException; @@ -31,18 +33,23 @@ protected FitBitOAuth2AccessToken createToken(String accessToken, String tokenTy * Related documentation: https://dev.fitbit.com/build/reference/web-api/oauth2/ */ @Override - public void generateError(String rawResponse) throws IOException { - final JsonNode errorNode = OAuth2AccessTokenJsonExtractor.OBJECT_MAPPER.readTree(rawResponse) - .get("errors").get(0); + public void generateError(Response response) throws IOException { + final JsonNode errorNode; + try { + errorNode = OAuth2AccessTokenJsonExtractor.OBJECT_MAPPER.readTree(response.getBody()).get("errors").get(0); + } catch (JsonProcessingException ex) { + throw new OAuth2AccessTokenErrorResponse(null, null, null, response); + } OAuth2Error errorCode; try { - errorCode = OAuth2Error.parseFrom(extractRequiredParameter(errorNode, "errorType", rawResponse).asText()); + errorCode = OAuth2Error + .parseFrom(extractRequiredParameter(errorNode, "errorType", response.getBody()).asText()); } catch (IllegalArgumentException iaE) { //non oauth standard error code errorCode = null; } - throw new OAuth2AccessTokenErrorResponse(errorCode, errorNode.get("message").asText(), null, rawResponse); + throw new OAuth2AccessTokenErrorResponse(errorCode, errorNode.get("message").asText(), null, response); } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/instagram/InstagramAccessTokenErrorResponse.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/instagram/InstagramAccessTokenErrorResponse.java new file mode 100644 index 000000000..bae3454a2 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/instagram/InstagramAccessTokenErrorResponse.java @@ -0,0 +1,84 @@ +package com.github.scribejava.apis.instagram; + +import com.github.scribejava.core.model.OAuthResponseException; +import java.io.IOException; +import java.util.Objects; +import com.github.scribejava.core.model.Response; + +/** + * non standard Instagram replace for {@link com.github.scribejava.core.model.OAuth2AccessTokenErrorResponse} + * + * examples:
+ * + * '{"error_type": "OAuthException", "code": 400, "error_message": "Missing required field client_id"}' + */ +public class InstagramAccessTokenErrorResponse extends OAuthResponseException { + + private static final long serialVersionUID = -1277129706699856895L; + + private final String errorType; + private final int code; + private final String errorMessage; + private final transient Response response; + + public InstagramAccessTokenErrorResponse(String errorType, int code, String errorMessage, Response response) + throws IOException { + super(response); + this.errorType = errorType; + this.code = code; + this.errorMessage = errorMessage; + this.response = response; + } + + public String getErrorType() { + return errorType; + } + + public int getCode() { + return code; + } + + public String getErrorMessage() { + return errorMessage; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + if (!super.equals(obj)) { + return false; + } + + final InstagramAccessTokenErrorResponse that = (InstagramAccessTokenErrorResponse) obj; + if (!Objects.equals(errorMessage, that.getErrorMessage())) { + return false; + } + return code == that.code && Objects.equals(errorType, that.errorType) + && Objects.equals(response, that.response); + } + + @Override + public int hashCode() { + int hash = super.hashCode(); + hash = 83 * hash + Objects.hashCode(response); + hash = 83 * hash + Objects.hashCode(errorMessage); + hash = 83 * hash + Objects.hashCode(errorType); + hash = 83 * hash + Objects.hashCode(code); + return hash; + } + + @Override + public String toString() { + return "InstagramAccessTokenErrorResponse{" + + "errorType='" + errorType + + "', code=" + code + + "', errorMessage='" + errorMessage + + "', response=" + response + + '}'; + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/instagram/InstagramAccessTokenJsonExtractor.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/instagram/InstagramAccessTokenJsonExtractor.java new file mode 100644 index 000000000..8f30b2e96 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/instagram/InstagramAccessTokenJsonExtractor.java @@ -0,0 +1,55 @@ +package com.github.scribejava.apis.instagram; + +import java.io.IOException; +import com.fasterxml.jackson.databind.JsonNode; +import com.github.scribejava.apis.facebook.FacebookAccessTokenJsonExtractor; +import com.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor; +import com.github.scribejava.core.model.Response; + +/** + * non standard Instagram Extractor + */ +public class InstagramAccessTokenJsonExtractor extends OAuth2AccessTokenJsonExtractor { + + protected InstagramAccessTokenJsonExtractor() { + } + + private static class InstanceHolder { + + private static final InstagramAccessTokenJsonExtractor INSTANCE = new InstagramAccessTokenJsonExtractor(); + } + + public static InstagramAccessTokenJsonExtractor instance() { + return InstanceHolder.INSTANCE; + } + + /** + * Non standard error message. Could be Instagram or Facebook specific. Usually Instagram type is used for getting + * access tokens. Facebook type is used for refreshing tokens. + * + * examples:
+ * + * Instagram specific: '{"error_type": "OAuthException", "code": 400, "error_message": "Missing required field + * client_id"}' + * + * Facebook specific: '{"error":{"message":"Error validating application. Invalid application + * ID.","type":"OAuthException","code":101,"fbtrace_id":"CvDR+X4WWIx"}}' + * + * @param response response + */ + @Override + public void generateError(Response response) throws IOException { + final JsonNode errorNode = OAuth2AccessTokenJsonExtractor.OBJECT_MAPPER.readTree(response.getBody()); + final JsonNode error = errorNode.get("error"); + if (error != null) { + FacebookAccessTokenJsonExtractor.instance().generateError(response); + } else { + throw new InstagramAccessTokenErrorResponse( + errorNode.get("error_type").asText(), + errorNode.get("code").asInt(), + errorNode.get("error_message").asText(), + response + ); + } + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/instagram/InstagramService.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/instagram/InstagramService.java new file mode 100644 index 000000000..5de1f3782 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/instagram/InstagramService.java @@ -0,0 +1,105 @@ +package com.github.scribejava.apis.instagram; + +import com.github.scribejava.apis.InstagramApi; +import java.io.IOException; +import java.io.OutputStream; +import java.util.concurrent.ExecutionException; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; +import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.model.OAuthAsyncRequestCallback; +import com.github.scribejava.core.model.OAuthConstants; +import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.model.Verb; +import com.github.scribejava.core.oauth.OAuth20Service; +import java.util.concurrent.Future; + +public class InstagramService extends OAuth20Service { + + public InstagramService(InstagramApi api, String apiKey, String apiSecret, String callback, String defaultScope, + String responseType, OutputStream debugStream, String userAgent, HttpClientConfig httpClientConfig, + HttpClient httpClient) { + super(api, apiKey, apiSecret, callback, defaultScope, responseType, debugStream, userAgent, httpClientConfig, + httpClient); + } + + /** + * Refresh a long-lived Instagram User Access Token that is at least 24 hours old but has not expired. Refreshed + * tokens are valid for 60 days from the date at which they are refreshed. + * + * @param accessToken long-lived access token + * @param scope (not used) + * @return refresh token request + */ + @Override + protected OAuthRequest createRefreshTokenRequest(String accessToken, String scope) { + if (accessToken == null || accessToken.isEmpty()) { + throw new IllegalArgumentException("The accessToken cannot be null or empty"); + } + final OAuthRequest request = new OAuthRequest(Verb.GET, getApi().getRefreshTokenEndpoint()); + + request.addParameter(OAuthConstants.GRANT_TYPE, "ig_refresh_token"); + request.addParameter(OAuthConstants.ACCESS_TOKEN, accessToken); + + logRequestWithParams("refresh token", request); + + return request; + } + + public Future getLongLivedAccessTokenAsync(OAuth2AccessToken accessToken) { + return getLongLivedAccessToken(accessToken.getAccessToken(), null); + } + + public Future getLongLivedAccessTokenAsync(String shortLivedAccessToken) { + return getLongLivedAccessToken(shortLivedAccessToken, null); + } + + public Future getLongLivedAccessToken(String shortLivedAccessToken, + OAuthAsyncRequestCallback callback) { + return sendAccessTokenRequestAsync(createLongLivedAccessTokenRequest(shortLivedAccessToken), callback); + } + + public Future getLongLivedAccessToken(OAuth2AccessToken accessToken, + OAuthAsyncRequestCallback callback) { + return getLongLivedAccessToken(accessToken.getAccessToken(), callback); + } + + /** + * Get long-lived access token. + * + * Initial accessToken is valid for 1 hour so one can get long-lived access token. Long-lived access token is valid + * for 60 days. + * + * @param accessToken short-lived access token + * @return long-lived access token with filled expireIn and refreshToken + * @throws java.lang.InterruptedException + * @throws java.util.concurrent.ExecutionException + * @throws java.io.IOException + */ + public OAuth2AccessToken getLongLivedAccessToken(OAuth2AccessToken accessToken) + throws InterruptedException, ExecutionException, IOException { + return getLongLivedAccessToken(accessToken.getAccessToken()); + } + + public OAuth2AccessToken getLongLivedAccessToken(String shortLivedAccessToken) + throws InterruptedException, ExecutionException, IOException { + final OAuthRequest request = createLongLivedAccessTokenRequest(shortLivedAccessToken); + return sendAccessTokenRequestSync(request); + } + + private OAuthRequest createLongLivedAccessTokenRequest(String shortLivedAccessToken) { + final OAuthRequest request = new OAuthRequest(Verb.GET, InstagramApi.LONG_LIVED_ACCESS_TOKEN_ENDPOINT); + + getApi().getClientAuthentication().addClientAuthentication(request, getApiKey(), getApiSecret()); + + request.addParameter(OAuthConstants.GRANT_TYPE, "ig_exchange_token"); + request.addParameter(OAuthConstants.ACCESS_TOKEN, shortLivedAccessToken); + + if (isDebug()) { + log("created long-lived access token request with body params [%s], query string params [%s]", + request.getBodyParams().asFormUrlEncodedString(), + request.getQueryStringParams().asFormUrlEncodedString()); + } + return request; + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/polar/PolarJsonTokenExtractor.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/polar/PolarJsonTokenExtractor.java index efca61631..1258dfe59 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/polar/PolarJsonTokenExtractor.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/polar/PolarJsonTokenExtractor.java @@ -1,10 +1,11 @@ package com.github.scribejava.apis.polar; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor; import com.github.scribejava.core.model.OAuth2AccessTokenErrorResponse; +import com.github.scribejava.core.model.Response; import com.github.scribejava.core.oauth2.OAuth2Error; - import java.io.IOException; /** @@ -32,18 +33,23 @@ protected PolarOAuth2AccessToken createToken(String accessToken, String tokenTyp } @Override - public void generateError(String rawResponse) throws IOException { - final JsonNode errorNode = OAuth2AccessTokenJsonExtractor.OBJECT_MAPPER.readTree(rawResponse) - .get("errors").get(0); + public void generateError(Response response) throws IOException { + final JsonNode errorNode; + try { + errorNode = OAuth2AccessTokenJsonExtractor.OBJECT_MAPPER.readTree(response.getBody()).get("errors").get(0); + } catch (JsonProcessingException ex) { + throw new OAuth2AccessTokenErrorResponse(null, null, null, response); + } OAuth2Error errorCode; try { - errorCode = OAuth2Error.parseFrom(extractRequiredParameter(errorNode, "errorType", rawResponse).asText()); + errorCode = OAuth2Error + .parseFrom(extractRequiredParameter(errorNode, "errorType", response.getBody()).asText()); } catch (IllegalArgumentException iaE) { //non oauth standard error code errorCode = null; } - throw new OAuth2AccessTokenErrorResponse(errorCode, errorNode.get("message").asText(), null, rawResponse); + throw new OAuth2AccessTokenErrorResponse(errorCode, errorNode.get("message").asText(), null, response); } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/polar/PolarOAuthService.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/polar/PolarOAuthService.java index e0b0af6f1..c016d6ac7 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/polar/PolarOAuthService.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/polar/PolarOAuthService.java @@ -1,6 +1,6 @@ package com.github.scribejava.apis.polar; -import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.apis.PolarAPI; import com.github.scribejava.core.httpclient.HttpClient; import com.github.scribejava.core.httpclient.HttpClientConfig; import com.github.scribejava.core.model.OAuthConstants; @@ -13,7 +13,7 @@ public class PolarOAuthService extends OAuth20Service { - public PolarOAuthService(DefaultApi20 api, String apiKey, String apiSecret, String callback, String defaultScope, + public PolarOAuthService(PolarAPI api, String apiKey, String apiSecret, String callback, String defaultScope, String responseType, OutputStream debugStream, String userAgent, HttpClientConfig httpClientConfig, HttpClient httpClient) { super(api, apiKey, apiSecret, callback, defaultScope, responseType, debugStream, userAgent, httpClientConfig, diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/slack/SlackJsonTokenExtractor.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/slack/SlackJsonTokenExtractor.java new file mode 100644 index 000000000..ca0be7587 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/slack/SlackJsonTokenExtractor.java @@ -0,0 +1,34 @@ +package com.github.scribejava.apis.slack; + +import com.fasterxml.jackson.databind.JsonNode; +import com.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor; + +public class SlackJsonTokenExtractor extends OAuth2AccessTokenJsonExtractor { + + protected SlackJsonTokenExtractor() { + } + + private static class InstanceHolder { + + private static final SlackJsonTokenExtractor INSTANCE = new SlackJsonTokenExtractor(); + } + + public static SlackJsonTokenExtractor instance() { + return SlackJsonTokenExtractor.InstanceHolder.INSTANCE; + } + + @Override + protected SlackOAuth2AccessToken createToken(String accessToken, String tokenType, Integer expiresIn, + String refreshToken, String scope, JsonNode response, String rawResponse) { + final String userAccessToken; + final JsonNode userAccessTokenNode = response.get("authed_user").get("access_token"); + if (userAccessTokenNode == null) { + userAccessToken = ""; + } else { + userAccessToken = userAccessTokenNode.asText(); + } + + return new SlackOAuth2AccessToken(accessToken, tokenType, expiresIn, refreshToken, scope, userAccessToken, + rawResponse); + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/slack/SlackOAuth2AccessToken.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/slack/SlackOAuth2AccessToken.java new file mode 100644 index 000000000..d1d28ad7b --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/slack/SlackOAuth2AccessToken.java @@ -0,0 +1,47 @@ +package com.github.scribejava.apis.slack; + +import com.github.scribejava.core.model.OAuth2AccessToken; + +import java.util.Objects; + +public class SlackOAuth2AccessToken extends OAuth2AccessToken { + + private static final long serialVersionUID = 1L; + + private final String userAccessToken; + + public SlackOAuth2AccessToken(String accessToken, String tokenType, Integer expiresIn, String refreshToken, + String scope, String userAccessToken, String rawResponse) { + super(accessToken, tokenType, expiresIn, refreshToken, scope, rawResponse); + this.userAccessToken = userAccessToken; + } + + public String getUserAccessToken() { + return userAccessToken; + } + + @Override + public int hashCode() { + int hash = super.hashCode(); + hash = 37 * hash + Objects.hashCode(userAccessToken); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + if (!super.equals(obj)) { + return false; + } + + return Objects.equals(userAccessToken, ((SlackOAuth2AccessToken) obj).getUserAccessToken()); + } +} diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/ExampleUtils.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/ExampleUtils.java index f5917bcda..5f60abcb1 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/ExampleUtils.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/ExampleUtils.java @@ -17,20 +17,7 @@ private ExampleUtils() { public static void turnOfSSl() { try { - final TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() { - @Override - public X509Certificate[] getAcceptedIssuers() { - return null; - } - - @Override - public void checkClientTrusted(X509Certificate[] certs, String authType) { - } - - @Override - public void checkServerTrusted(X509Certificate[] certs, String authType) { - } - } + final TrustManager[] trustAllCerts = new TrustManager[]{new TrustAllCertsManager() }; final SSLContext sc = SSLContext.getInstance("SSL"); @@ -49,4 +36,20 @@ public boolean verify(String hostname, SSLSession session) { throw new RuntimeException(e); } } + + private static class TrustAllCertsManager implements X509TrustManager { + + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + @Override + public void checkClientTrusted(X509Certificate[] certs, String authType) { + } + + @Override + public void checkServerTrusted(X509Certificate[] certs, String authType) { + } + } } diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/InstagramExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/InstagramExample.java new file mode 100644 index 000000000..9694ba909 --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/InstagramExample.java @@ -0,0 +1,97 @@ +package com.github.scribejava.apis.examples; + +import java.io.IOException; +import java.util.Random; +import java.util.Scanner; +import java.util.concurrent.ExecutionException; +import com.github.scribejava.apis.InstagramApi; +import com.github.scribejava.apis.instagram.InstagramService; +import com.github.scribejava.core.builder.ServiceBuilder; +import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.model.Response; +import com.github.scribejava.core.model.Verb; +import com.github.scribejava.core.oauth.OAuth20Service; + +public class InstagramExample { + + private static final String NETWORK_NAME = "Instagram"; + private static final String PROTECTED_RESOURCE_URL + = "https://graph.instagram.com/me/media?fields=id,caption,media_type,media_url,username"; + + private InstagramExample() { + } + + @SuppressWarnings("PMD.SystemPrintln") + public static void main(String... args) throws IOException, InterruptedException, ExecutionException { + // Replace these with your client id and secret + final String clientId = "client-id"; + final String clientSecret = "client-secret"; + final String secretState = "secret" + new Random().nextInt(999_999); + final OAuth20Service service = new ServiceBuilder(clientId) + .apiSecret(clientSecret) + .defaultScope("user_profile,user_media") + .callback("http://example.com") + .build(InstagramApi.instance()); + + final Scanner in = new Scanner(System.in, "UTF-8"); + + System.out.println("=== " + NETWORK_NAME + "'s OAuth Workflow ==="); + System.out.println(); + + // Obtain the Authorization URL + System.out.println("Fetching the Authorization URL..."); + final String authorizationUrl = service.getAuthorizationUrl(secretState); + System.out.println("Got the Authorization URL!"); + System.out.println("Now go and authorize ScribeJava here:"); + System.out.println(authorizationUrl); + System.out.println("And paste the authorization code here"); + System.out.print(">>"); + final String code = in.nextLine(); + System.out.println(); + + System.out.println("And paste the state from server here. We have set 'secretState'='" + secretState + "'."); + System.out.print(">>"); + final String value = in.nextLine(); + if (secretState.equals(value)) { + System.out.println("State value does match!"); + } else { + System.out.println("Ooops, state value does not match!"); + System.out.println("Expected = " + secretState); + System.out.println("Got = " + value); + System.out.println(); + } + + System.out.println("Trading the Authorization Code for an Access Token..."); + final OAuth2AccessToken accessToken = service.getAccessToken(code); + System.out.println("Got the Access Token!"); + System.out.println("(The raw response looks like this: " + accessToken.getRawResponse() + "')"); + 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..."); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); + service.signRequest(accessToken, request); + try (Response response = service.execute(request)) { + 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 ScribeJava! :)"); + + final InstagramService instagramService = (InstagramService) service; + System.out.println("Now let's exchange our short-lived token to long-lived access token..."); + final OAuth2AccessToken longLivedAccessToken = instagramService.getLongLivedAccessToken(accessToken); + System.out.println("Got the Access Token!"); + System.out.println("(The access token raw response looks like this: " + longLivedAccessToken.getRawResponse() + + "')"); + + System.out.println("Now it's time to refresh long-lived token..."); + final OAuth2AccessToken refreshAccessToken = service.refreshAccessToken(longLivedAccessToken.getAccessToken()); + System.out.println("Got the refreshed Access Token!"); + System.out.println("(The refreshed access token raw response looks like this: " + + refreshAccessToken.getRawResponse() + "')"); + } +} diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/LinkedIn20Example.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/LinkedIn20Example.java index 9c5083386..05e6733df 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/LinkedIn20Example.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/LinkedIn20Example.java @@ -3,6 +3,7 @@ import java.util.Scanner; import com.github.scribejava.core.builder.ServiceBuilder; import com.github.scribejava.apis.LinkedInApi20; +import com.github.scribejava.core.builder.ScopeBuilder; import com.github.scribejava.core.model.OAuth2AccessToken; import com.github.scribejava.core.model.OAuthRequest; import com.github.scribejava.core.model.Response; @@ -29,7 +30,7 @@ public static void main(String... args) throws IOException, InterruptedException final String clientSecret = "your client secret"; final OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) - .defaultScope("r_liteprofile r_emailaddress") // replace with desired scope + .defaultScope(new ScopeBuilder("r_liteprofile", "r_emailaddress")) // replace with desired scope .callback("http://example.com/callback") .build(LinkedInApi20.instance()); final Scanner in = new Scanner(System.in); diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SlackExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SlackExample.java new file mode 100644 index 000000000..44a952d16 --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SlackExample.java @@ -0,0 +1,102 @@ +package com.github.scribejava.apis.examples; + +import com.github.scribejava.apis.SlackApi; +import com.github.scribejava.apis.slack.SlackOAuth2AccessToken; +import com.github.scribejava.core.builder.ServiceBuilder; +import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.model.Response; +import com.github.scribejava.core.model.Verb; +import com.github.scribejava.core.oauth.OAuth20Service; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Scanner; +import java.util.concurrent.ExecutionException; + +public class SlackExample { + + private static final String NETWORK_NAME = "Slack.com"; + private static final String BOT_RESOURCE_URL = "https://slack.com/api/channels.list"; + private static final String BOT_SCOPE = "channels:read"; + private static final String USER_RESOURCE_URL = "https://slack.com/api/users.list"; + private static final String USER_SCOPE = "users:read"; + private static final String PAYLOAD = "null"; + private static final String CONTENT_TYPE_NAME = "Content-Type"; + private static final String CONTENT_TYPE_VALUE = "application/json"; + + private SlackExample() { + } + + @SuppressWarnings("PMD.SystemPrintln") + public static void main(String... args) throws IOException, InterruptedException, ExecutionException { + // Replace these with your client id and secret + final String clientId = "client-id"; + final String clientSecret = "client-secret"; + final OAuth20Service service = new ServiceBuilder(clientId) + .apiSecret(clientSecret) + .callback("https://www.example.com/oauth_callback/") + .defaultScope(BOT_SCOPE) + .build(SlackApi.instance()); + + final 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..."); + + final Map additionalParams = new HashMap<>(); + // define user scope if any + additionalParams.put("user_scope", USER_SCOPE); + final String authorizationUrl = service.createAuthorizationUrlBuilder() + .additionalParams(additionalParams) + .build(); + System.out.println("Got the Authorization URL!"); + System.out.println("Now go and authorize ScribeJava here:"); + System.out.println(authorizationUrl); + System.out.println("And paste the authorization code here"); + System.out.print(">>"); + final String code = in.nextLine(); + System.out.println(); + + System.out.println("Trading the Authorization Code for an Access Token..."); + final OAuth2AccessToken accessToken = service.getAccessToken(code); + System.out.println("Got the Access Token!"); + System.out.println("(The raw response looks like this: " + accessToken.getRawResponse() + "')"); + System.out.println(); + + System.out.println("Getting info using BOT token..."); + final OAuthRequest request = new OAuthRequest(Verb.GET, BOT_RESOURCE_URL); + request.addHeader(CONTENT_TYPE_NAME, CONTENT_TYPE_VALUE); + request.setPayload(PAYLOAD); + service.signRequest(accessToken, request); + + try (Response response = service.execute(request)) { + 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("Getting info using USER token..."); + final OAuthRequest userRequest = new OAuthRequest(Verb.GET, USER_RESOURCE_URL); + userRequest.addHeader(CONTENT_TYPE_NAME, CONTENT_TYPE_VALUE); + userRequest.setPayload(PAYLOAD); + final SlackOAuth2AccessToken token = (SlackOAuth2AccessToken) accessToken; + service.signRequest(token.getUserAccessToken(), userRequest); + + try (Response response = service.execute(userRequest)) { + 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 ScribeJava! :)"); + } +} diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FacebookAsyncApacheExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteAsyncApacheExample.java similarity index 89% rename from scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FacebookAsyncApacheExample.java rename to scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteAsyncApacheExample.java index 29d8dba96..392348b2b 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FacebookAsyncApacheExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteAsyncApacheExample.java @@ -3,7 +3,7 @@ import java.util.Random; import java.util.Scanner; import java.util.concurrent.ExecutionException; -import com.github.scribejava.apis.FacebookApi; +import com.github.scribejava.apis.VkontakteApi; import com.github.scribejava.core.builder.ServiceBuilder; import com.github.scribejava.core.model.OAuth2AccessToken; import com.github.scribejava.core.model.OAuthRequest; @@ -13,12 +13,13 @@ import com.github.scribejava.httpclient.apache.ApacheHttpClientConfig; import java.io.IOException; -public class FacebookAsyncApacheExample { +public class VkontakteAsyncApacheExample { - private static final String NETWORK_NAME = "Facebook"; - private static final String PROTECTED_RESOURCE_URL = "https://graph.facebook.com/v3.2/me"; + private static final String NETWORK_NAME = "vk.com"; + private static final String PROTECTED_RESOURCE_URL = "https://api.vk.com/method/users.get?v=" + + VkontakteApi.VERSION; - private FacebookAsyncApacheExample() { + private VkontakteAsyncApacheExample() { } @SuppressWarnings("PMD.SystemPrintln") @@ -28,11 +29,11 @@ public static void main(String... args) throws InterruptedException, ExecutionEx final String clientSecret = "your client secret"; final String secretState = "secret" + new Random().nextInt(999_999); - try (OAuth20Service service = new ServiceBuilder(clientId) + try ( OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) .callback("http://www.example.com/oauth_callback/") .httpClientConfig(ApacheHttpClientConfig.defaultConfig()) - .build(FacebookApi.instance())) { + .build(VkontakteApi.instance())) { final Scanner in = new Scanner(System.in, "UTF-8"); System.out.println("=== " + NETWORK_NAME + "'s Async OAuth Workflow ==="); @@ -73,7 +74,7 @@ public static void main(String... args) throws InterruptedException, ExecutionEx System.out.println("Now we're going to access a protected resource..."); final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - try (Response response = service.execute(request)) { + try ( Response response = service.execute(request)) { System.out.println("Got it! Lets see what we found..."); System.out.println(); System.out.println(response.getCode()); diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FacebookAsyncNingExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteAsyncNingExample.java similarity index 90% rename from scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FacebookAsyncNingExample.java rename to scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteAsyncNingExample.java index 29c52cbad..bfb80d1a9 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FacebookAsyncNingExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteAsyncNingExample.java @@ -5,7 +5,7 @@ import java.util.Random; import java.util.Scanner; import java.util.concurrent.ExecutionException; -import com.github.scribejava.apis.FacebookApi; +import com.github.scribejava.apis.VkontakteApi; import com.github.scribejava.core.builder.ServiceBuilder; import com.github.scribejava.core.model.OAuth2AccessToken; import com.github.scribejava.core.model.OAuthRequest; @@ -14,12 +14,13 @@ import com.github.scribejava.core.oauth.OAuth20Service; import java.io.IOException; -public class FacebookAsyncNingExample { +public class VkontakteAsyncNingExample { - private static final String NETWORK_NAME = "Facebook"; - private static final String PROTECTED_RESOURCE_URL = "https://graph.facebook.com/v3.2/me"; + private static final String NETWORK_NAME = "vk.com"; + private static final String PROTECTED_RESOURCE_URL = "https://api.vk.com/method/users.get?v=" + + VkontakteApi.VERSION; - private FacebookAsyncNingExample() { + private VkontakteAsyncNingExample() { } @SuppressWarnings("PMD.SystemPrintln") @@ -36,11 +37,11 @@ public static void main(String... args) throws InterruptedException, ExecutionEx .setReadTimeout(1_000) .build()); - try (OAuth20Service service = new ServiceBuilder(clientId) + try ( OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) .callback("http://www.example.com/oauth_callback/") .httpClientConfig(clientConfig) - .build(FacebookApi.instance())) { + .build(VkontakteApi.instance())) { final Scanner in = new Scanner(System.in, "UTF-8"); System.out.println("=== " + NETWORK_NAME + "'s Async OAuth Workflow ==="); @@ -81,7 +82,7 @@ public static void main(String... args) throws InterruptedException, ExecutionEx System.out.println("Now we're going to access a protected resource..."); final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - try (Response response = service.execute(request)) { + try ( Response response = service.execute(request)) { System.out.println("Got it! Lets see what we found..."); System.out.println(); System.out.println(response.getCode()); diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteClientCredentialsGrantExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteClientCredentialsGrantExample.java index 2b6e2d4f7..3e4d2d363 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteClientCredentialsGrantExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteClientCredentialsGrantExample.java @@ -9,7 +9,7 @@ public class VkontakteClientCredentialsGrantExample { - private static final String NETWORK_NAME = "Vkontakte.ru"; + private static final String NETWORK_NAME = "vk.com>"; private VkontakteClientCredentialsGrantExample() { } diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteExample.java index baa4dae91..ca1a698bf 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteExample.java @@ -15,7 +15,7 @@ public class VkontakteExample { - private static final String NETWORK_NAME = "Vkontakte.ru"; + private static final String NETWORK_NAME = "vk.com"; private static final String PROTECTED_RESOURCE_URL = "https://api.vk.com/method/users.get?v=" + VkontakteApi.VERSION; @@ -66,7 +66,7 @@ public static void main(String... args) throws IOException, InterruptedException System.out.println("Now we're going to access a protected resource..."); final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - try (Response response = service.execute(request)) { + try ( Response response = service.execute(request)) { System.out.println("Got it! Lets see what we found..."); System.out.println(); System.out.println(response.getCode()); diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteExternalHttpExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteExternalHttpExample.java index 7f9e0c969..fe5340294 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteExternalHttpExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteExternalHttpExample.java @@ -16,7 +16,7 @@ public class VkontakteExternalHttpExample { - private static final String NETWORK_NAME = "Vkontakte.ru"; + private static final String NETWORK_NAME = "vk.com"; private static final String PROTECTED_RESOURCE_URL = "https://api.vk.com/method/users.get?v=" + VkontakteApi.VERSION; @@ -37,7 +37,7 @@ public static void main(String... args) throws IOException, InterruptedException .setReadTimeout(1_000) .build(); //wrap it - try (DefaultAsyncHttpClient ahcHttpClient = new DefaultAsyncHttpClient(httpClientConfig)) { + try ( DefaultAsyncHttpClient ahcHttpClient = new DefaultAsyncHttpClient(httpClientConfig)) { //wrap it final AhcHttpClient wrappedAHCHttpClient = new AhcHttpClient(ahcHttpClient); @@ -74,7 +74,7 @@ public static void main(String... args) throws IOException, InterruptedException System.out.println("Now we're going to access a protected resource..."); final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - try (Response response = service.execute(request)) { + try ( Response response = service.execute(request)) { System.out.println("Got it! Lets see what we found..."); System.out.println(); System.out.println(response.getCode()); diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/facebook/FacebookAccessTokenJsonExtractorTest.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/facebook/FacebookAccessTokenJsonExtractorTest.java index cb9620e68..240f08b81 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/facebook/FacebookAccessTokenJsonExtractorTest.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/facebook/FacebookAccessTokenJsonExtractorTest.java @@ -4,8 +4,9 @@ import java.io.IOException; import java.util.Collections; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.Assert.assertThrows; import org.junit.Test; +import org.junit.function.ThrowingRunnable; public class FacebookAccessTokenJsonExtractorTest { @@ -19,14 +20,20 @@ public void shouldThrowExceptionIfResponseIsError() throws IOException { + "\"code\":100," + "\"fbtrace_id\":\"DtxvtGRaxbB\"}}"; try (Response response = error(body)) { - extractor.extract(response); - fail(); - } catch (FacebookAccessTokenErrorResponse fateR) { - assertEquals("This authorization code has been used.", fateR.getMessage()); + + final FacebookAccessTokenErrorResponse fateR = assertThrows(FacebookAccessTokenErrorResponse.class, + new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(response); + } + }); + + assertEquals("This authorization code has been used.", fateR.getErrorMessage()); assertEquals("OAuthException", fateR.getType()); assertEquals(100, fateR.getCodeInt()); assertEquals("DtxvtGRaxbB", fateR.getFbtraceId()); - assertEquals(body, fateR.getRawResponse()); + assertEquals(body, fateR.getResponse().getBody()); } } diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/fitbit/FitBitJsonTokenExtractorTest.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/fitbit/FitBitJsonTokenExtractorTest.java index 396377294..836a0d6ba 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/fitbit/FitBitJsonTokenExtractorTest.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/fitbit/FitBitJsonTokenExtractorTest.java @@ -1,6 +1,7 @@ package com.github.scribejava.apis.fitbit; import com.github.scribejava.core.model.OAuth2AccessTokenErrorResponse; +import com.github.scribejava.core.model.Response; import com.github.scribejava.core.oauth2.OAuth2Error; import java.io.IOException; @@ -28,7 +29,7 @@ public void testErrorExtraction() throws IOException { new ThrowingRunnable() { @Override public void run() throws Throwable { - extractor.generateError(ERROR_JSON); + extractor.generateError(new Response(403, null, null, ERROR_JSON)); } }); assertSame(OAuth2Error.INVALID_GRANT, thrown.getError()); diff --git a/scribejava-core/pom.xml b/scribejava-core/pom.xml index f3c027aaa..d5fbfc488 100644 --- a/scribejava-core/pom.xml +++ b/scribejava-core/pom.xml @@ -5,15 +5,41 @@ com.github.scribejava scribejava - 8.0.0 + 8.3.4-SNAPSHOT ../pom.xml - + com.github.scribejava scribejava-core ScribeJava Core jar + + + com.github.scribejava + scribejava-java8 + ${project.version} + + + commons-codec + commons-codec + 1.15 + true + + + jakarta.xml.bind + jakarta.xml.bind-api + 4.0.0 + true + + + javax.xml.bind + jaxb-api + 2.3.0 + true + + + diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/base64/Base64.java b/scribejava-core/src/main/java/com/github/scribejava/core/base64/Base64.java new file mode 100644 index 000000000..18a8f01a0 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/base64/Base64.java @@ -0,0 +1,55 @@ +package com.github.scribejava.core.base64; + +public abstract class Base64 { + + private static volatile Base64 instance; + + public static Base64 getInstance() { + Base64 localInstance = instance; + if (localInstance == null) { + synchronized (Base64.class) { + localInstance = instance; + if (localInstance == null) { + localInstance = createInstance(); + instance = localInstance; + } + } + } + return localInstance; + } + + private static Base64 createInstance() { + if (Java8Base64.isAvailable()) { + return new Java8Base64(); + } + if (Jaxb230Base64.isAvailable()) { + return new Jaxb230Base64(); + } + if (JaxbBase64.isAvailable()) { + return new JaxbBase64(); + } + if (CommonsCodecBase64.isAvailable()) { + return new CommonsCodecBase64(); + } + throw new IllegalStateException( + "No Base64 implementation was provided. Java 8 Base64, Apache Commons Codec or JAXB is needed"); + } + + public static void init(Base64 base64) { + synchronized (Base64.class) { + instance = base64; + } + } + + public static String encode(byte[] bytes) { + return getInstance().internalEncode(bytes); + } + + public static String encodeUrlWithoutPadding(byte[] bytes) { + return getInstance().internalEncodeUrlWithoutPadding(bytes); + } + + protected abstract String internalEncode(byte[] bytes); + + protected abstract String internalEncodeUrlWithoutPadding(byte[] bytes); +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/base64/CommonsCodecBase64.java b/scribejava-core/src/main/java/com/github/scribejava/core/base64/CommonsCodecBase64.java new file mode 100644 index 000000000..bb172d720 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/base64/CommonsCodecBase64.java @@ -0,0 +1,36 @@ +package com.github.scribejava.core.base64; + +public class CommonsCodecBase64 extends Base64 { + + private static final org.apache.commons.codec.binary.Base64 BASE64_ENCODER; + private static final org.apache.commons.codec.binary.Base64 BASE64_URL_ENCODER_WITHOUT_PADDING; + + static { + if (isAvailable()) { + BASE64_ENCODER = new org.apache.commons.codec.binary.Base64(); + BASE64_URL_ENCODER_WITHOUT_PADDING = new org.apache.commons.codec.binary.Base64(0, null, true); + } else { + BASE64_ENCODER = null; + BASE64_URL_ENCODER_WITHOUT_PADDING = null; + } + } + + @Override + protected String internalEncode(byte[] bytes) { + return BASE64_ENCODER.encodeToString(bytes); + } + + @Override + protected String internalEncodeUrlWithoutPadding(byte[] bytes) { + return BASE64_URL_ENCODER_WITHOUT_PADDING.encodeToString(bytes); + } + + static boolean isAvailable() { + try { + Class.forName("org.apache.commons.codec.binary.Base64", false, CommonsCodecBase64.class.getClassLoader()); + return true; + } catch (ClassNotFoundException cnfE) { + return false; + } + } +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/base64/Java8Base64.java b/scribejava-core/src/main/java/com/github/scribejava/core/base64/Java8Base64.java new file mode 100644 index 000000000..8e4c6c990 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/base64/Java8Base64.java @@ -0,0 +1,26 @@ +package com.github.scribejava.core.base64; + +public class Java8Base64 extends Base64 { + + private static final com.github.scribejava.java8.base64.Java8Base64 JAVA8_BASE64 + = isAvailable() ? new com.github.scribejava.java8.base64.Java8Base64() : null; + + @Override + protected String internalEncode(byte[] bytes) { + return JAVA8_BASE64.internalEncode(bytes); + } + + @Override + protected String internalEncodeUrlWithoutPadding(byte[] bytes) { + return JAVA8_BASE64.internalEncodeUrlWithoutPadding(bytes); + } + + static boolean isAvailable() { + try { + Class.forName("java.util.Base64", false, Java8Base64.class.getClassLoader()); + return true; + } catch (ClassNotFoundException cnfE) { + return false; + } + } +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/base64/Jaxb230Base64.java b/scribejava-core/src/main/java/com/github/scribejava/core/base64/Jaxb230Base64.java new file mode 100644 index 000000000..c4ee4d799 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/base64/Jaxb230Base64.java @@ -0,0 +1,32 @@ +package com.github.scribejava.core.base64; + +import javax.xml.bind.DatatypeConverter; + +/** + * JAXB v2.3.0 (the latest for JRE 7) + */ +public class Jaxb230Base64 extends Base64 { + + @Override + protected String internalEncode(byte[] bytes) { + return DatatypeConverter.printBase64Binary(bytes); + } + + @Override + protected String internalEncodeUrlWithoutPadding(byte[] bytes) { + String string = DatatypeConverter.printBase64Binary(bytes); + while (string.endsWith("=")) { + string = string.substring(0, string.length() - 1); + } + return string.replace('+', '-').replace('/', '_'); + } + + static boolean isAvailable() { + try { + Class.forName("javax.xml.bind.DatatypeConverter", false, Jaxb230Base64.class.getClassLoader()); + return true; + } catch (ClassNotFoundException cnfE) { + return false; + } + } +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/base64/JaxbBase64.java b/scribejava-core/src/main/java/com/github/scribejava/core/base64/JaxbBase64.java new file mode 100644 index 000000000..85e889501 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/base64/JaxbBase64.java @@ -0,0 +1,29 @@ +package com.github.scribejava.core.base64; + +import jakarta.xml.bind.DatatypeConverter; + +public class JaxbBase64 extends Base64 { + + @Override + protected String internalEncode(byte[] bytes) { + return DatatypeConverter.printBase64Binary(bytes); + } + + @Override + protected String internalEncodeUrlWithoutPadding(byte[] bytes) { + String string = DatatypeConverter.printBase64Binary(bytes); + while (string.endsWith("=")) { + string = string.substring(0, string.length() - 1); + } + return string.replace('+', '-').replace('/', '_'); + } + + static boolean isAvailable() { + try { + Class.forName("jakarta.xml.bind.DatatypeConverter", false, JaxbBase64.class.getClassLoader()); + return true; + } catch (ClassNotFoundException cnfE) { + return false; + } + } +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/builder/ScopeBuilder.java b/scribejava-core/src/main/java/com/github/scribejava/core/builder/ScopeBuilder.java new file mode 100644 index 000000000..8bd22e1a4 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/builder/ScopeBuilder.java @@ -0,0 +1,56 @@ +package com.github.scribejava.core.builder; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +/** + * OAuth2.0 Scope Builder. It allows specifying multiple scopes one by one. It will combine them in the single + * space-delimited string. OAuth 2.0 standard specifies space as a delimiter for scopes + * (https://tools.ietf.org/html/rfc6749#section-3.3). If you found API, that do not support spaces, but support + * something else, let ScribeJava know (submit the issue here https://github.com/scribejava/scribejava/issues) and use + * your own concatenated string as a temporary workaround. + */ +public class ScopeBuilder { + + private final Set scopes = new HashSet<>(); + + public ScopeBuilder() { + } + + public ScopeBuilder(String scope) { + withScope(scope); + } + + public ScopeBuilder(String... scopes) { + withScopes(scopes); + } + + public ScopeBuilder(Collection scopes) { + withScopes(scopes); + } + + public final ScopeBuilder withScope(String scope) { + scopes.add(scope); + return this; + } + + public final ScopeBuilder withScopes(String... scopes) { + this.scopes.addAll(Arrays.asList(scopes)); + return this; + } + + public final ScopeBuilder withScopes(Collection scopes) { + this.scopes.addAll(scopes); + return this; + } + + public String build() { + final StringBuilder scopeBuilder = new StringBuilder(); + for (String scope : scopes) { + scopeBuilder.append(' ').append(scope); + } + return scopeBuilder.substring(1); + } +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/builder/ServiceBuilder.java b/scribejava-core/src/main/java/com/github/scribejava/core/builder/ServiceBuilder.java index c35e0429d..eade1ede5 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/builder/ServiceBuilder.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/builder/ServiceBuilder.java @@ -51,6 +51,12 @@ public ServiceBuilder apiSecret(String apiSecret) { return this; } + @Override + public ServiceBuilder apiSecretIsEmptyStringUnsafe() { + apiSecret = ""; + return this; + } + private ServiceBuilder setScope(String scope) { Preconditions.checkEmptyString(scope, "Invalid OAuth scope"); this.scope = scope; @@ -62,6 +68,11 @@ public ServiceBuilderOAuth20 defaultScope(String defaultScope) { return setScope(defaultScope); } + @Override + public ServiceBuilderOAuth20 defaultScope(ScopeBuilder scopeBuilder) { + return setScope(scopeBuilder.build()); + } + @Override public ServiceBuilderOAuth10a withScope(String scope) { return setScope(scope); diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/builder/ServiceBuilderCommon.java b/scribejava-core/src/main/java/com/github/scribejava/core/builder/ServiceBuilderCommon.java index ba36cdef0..fd71c49d8 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/builder/ServiceBuilderCommon.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/builder/ServiceBuilderCommon.java @@ -35,6 +35,16 @@ public interface ServiceBuilderCommon { */ ServiceBuilderCommon apiSecret(String apiSecret); + /** + * Configures the api secret as "" (empty string). + * + * Used usually for a test environments or another strange cases. Not all providers support empty string as api key + * and will throw an Exception in such cases. + * + * @return the {@link ServiceBuilder} instance for method chaining + */ + ServiceBuilderCommon apiSecretIsEmptyStringUnsafe(); + ServiceBuilderCommon httpClientConfig(HttpClientConfig httpClientConfig); /** diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/builder/ServiceBuilderOAuth10a.java b/scribejava-core/src/main/java/com/github/scribejava/core/builder/ServiceBuilderOAuth10a.java index d51f81f4b..8c7027cca 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/builder/ServiceBuilderOAuth10a.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/builder/ServiceBuilderOAuth10a.java @@ -17,6 +17,9 @@ public interface ServiceBuilderOAuth10a extends ServiceBuilderCommon { @Override ServiceBuilderOAuth10a apiSecret(String apiSecret); + @Override + ServiceBuilderOAuth10a apiSecretIsEmptyStringUnsafe(); + @Override ServiceBuilderOAuth10a httpClientConfig(HttpClientConfig httpClientConfig); diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/builder/ServiceBuilderOAuth20.java b/scribejava-core/src/main/java/com/github/scribejava/core/builder/ServiceBuilderOAuth20.java index 47a9cf3f6..bcf119db9 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/builder/ServiceBuilderOAuth20.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/builder/ServiceBuilderOAuth20.java @@ -17,6 +17,9 @@ public interface ServiceBuilderOAuth20 extends ServiceBuilderCommon { @Override ServiceBuilderOAuth20 apiSecret(String apiSecret); + @Override + ServiceBuilderOAuth20 apiSecretIsEmptyStringUnsafe(); + @Override ServiceBuilderOAuth20 httpClientConfig(HttpClientConfig httpClientConfig); @@ -48,5 +51,7 @@ public interface ServiceBuilderOAuth20 extends ServiceBuilderCommon { */ ServiceBuilderOAuth20 defaultScope(String defaultScope); + ServiceBuilderOAuth20 defaultScope(ScopeBuilder scopeBuilder); + OAuth20Service build(DefaultApi20 api); } diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/extractors/DeviceAuthorizationJsonExtractor.java b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/DeviceAuthorizationJsonExtractor.java index 14e09f28d..5b4fa248b 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/extractors/DeviceAuthorizationJsonExtractor.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/DeviceAuthorizationJsonExtractor.java @@ -22,17 +22,14 @@ public static DeviceAuthorizationJsonExtractor instance() { } public DeviceAuthorization extract(Response response) throws IOException { - - final String body = response.getBody(); - if (response.getCode() != 200) { - generateError(body); + generateError(response); } - return createDeviceAuthorization(body); + return createDeviceAuthorization(response.getBody()); } - public void generateError(String rawResponse) throws IOException { - OAuth2AccessTokenJsonExtractor.instance().generateError(rawResponse); + public void generateError(Response response) throws IOException { + OAuth2AccessTokenJsonExtractor.instance().generateError(response); } private DeviceAuthorization createDeviceAuthorization(String rawResponse) throws IOException { diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/extractors/OAuth2AccessTokenJsonExtractor.java b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/OAuth2AccessTokenJsonExtractor.java index 96ff82de5..b9bfd6cba 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/extractors/OAuth2AccessTokenJsonExtractor.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/OAuth2AccessTokenJsonExtractor.java @@ -1,5 +1,6 @@ package com.github.scribejava.core.extractors; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import java.io.IOException; import java.net.URI; @@ -33,7 +34,7 @@ public OAuth2AccessToken extract(Response response) throws IOException { Preconditions.checkEmptyString(body, "Response body is incorrect. Can't extract a token from an empty string"); if (response.getCode() != 200) { - generateError(body); + generateError(response); } return createToken(body); } @@ -41,13 +42,20 @@ public OAuth2AccessToken extract(Response response) throws IOException { /** * Related documentation: https://tools.ietf.org/html/rfc6749#section-5.2 * - * @param rawResponse response - * @throws IOException IOException + * @param response response + * @throws java.io.IOException IOException + * */ - public void generateError(String rawResponse) throws IOException { - final JsonNode response = OBJECT_MAPPER.readTree(rawResponse); + public void generateError(Response response) throws IOException { + final String responseBody = response.getBody(); + final JsonNode responseBodyJson; + try { + responseBodyJson = OBJECT_MAPPER.readTree(responseBody); + } catch (JsonProcessingException ex) { + throw new OAuth2AccessTokenErrorResponse(null, null, null, response); + } - final JsonNode errorUriInString = response.get("error_uri"); + final JsonNode errorUriInString = responseBodyJson.get("error_uri"); URI errorUri; try { errorUri = errorUriInString == null ? null : URI.create(errorUriInString.asText()); @@ -57,16 +65,17 @@ public void generateError(String rawResponse) throws IOException { OAuth2Error errorCode; try { - errorCode = OAuth2Error.parseFrom(extractRequiredParameter(response, "error", rawResponse).asText()); + errorCode = OAuth2Error + .parseFrom(extractRequiredParameter(responseBodyJson, "error", responseBody).asText()); } catch (IllegalArgumentException iaE) { //non oauth standard error code errorCode = null; } - final JsonNode errorDescription = response.get("error_description"); + final JsonNode errorDescription = responseBodyJson.get("error_description"); throw new OAuth2AccessTokenErrorResponse(errorCode, errorDescription == null ? null : errorDescription.asText(), - errorUri, rawResponse); + errorUri, response); } private OAuth2AccessToken createToken(String rawResponse) throws IOException { diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/AbstractAsyncOnlyHttpClient.java b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/AbstractAsyncOnlyHttpClient.java index 48917eb60..10b3d5faa 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/AbstractAsyncOnlyHttpClient.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/AbstractAsyncOnlyHttpClient.java @@ -1,6 +1,6 @@ package com.github.scribejava.core.httpclient; -import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.model.OAuthAsyncRequestCallback; import com.github.scribejava.core.model.Response; import com.github.scribejava.core.model.Verb; import java.io.File; @@ -14,8 +14,17 @@ public abstract class AbstractAsyncOnlyHttpClient implements HttpClient { public Response execute(String userAgent, Map headers, Verb httpVerb, String completeUrl, byte[] bodyContents) throws InterruptedException, ExecutionException, IOException { - return executeAsync(userAgent, headers, httpVerb, completeUrl, bodyContents, null, - (OAuthRequest.ResponseConverter) null).get(); + final OAuthAsyncRequestThrowableHolderCallback oAuthAsyncRequestThrowableHolderCallback + = new OAuthAsyncRequestThrowableHolderCallback(); + + final Response response = executeAsync(userAgent, headers, httpVerb, completeUrl, bodyContents, + oAuthAsyncRequestThrowableHolderCallback, null).get(); + + final Throwable throwable = oAuthAsyncRequestThrowableHolderCallback.getThrowable(); + if (throwable != null) { + throw new ExecutionException(throwable); + } + return response; } @Override @@ -23,23 +32,71 @@ public Response execute(String userAgent, Map headers, Verb http com.github.scribejava.core.httpclient.multipart.MultipartPayload bodyContents) throws InterruptedException, ExecutionException, IOException { - return executeAsync(userAgent, headers, httpVerb, completeUrl, bodyContents, null, - (OAuthRequest.ResponseConverter) null).get(); + final OAuthAsyncRequestThrowableHolderCallback oAuthAsyncRequestThrowableHolderCallback + = new OAuthAsyncRequestThrowableHolderCallback(); + + final Response response = executeAsync(userAgent, headers, httpVerb, completeUrl, bodyContents, + oAuthAsyncRequestThrowableHolderCallback, null).get(); + + final Throwable throwable = oAuthAsyncRequestThrowableHolderCallback.getThrowable(); + if (throwable != null) { + throw new ExecutionException(throwable); + } + + return response; } @Override public Response execute(String userAgent, Map headers, Verb httpVerb, String completeUrl, String bodyContents) throws InterruptedException, ExecutionException, IOException { - return executeAsync(userAgent, headers, httpVerb, completeUrl, bodyContents, null, - (OAuthRequest.ResponseConverter) null).get(); + final OAuthAsyncRequestThrowableHolderCallback oAuthAsyncRequestThrowableHolderCallback + = new OAuthAsyncRequestThrowableHolderCallback(); + + final Response response = executeAsync(userAgent, headers, httpVerb, completeUrl, bodyContents, + oAuthAsyncRequestThrowableHolderCallback, null).get(); + + final Throwable throwable = oAuthAsyncRequestThrowableHolderCallback.getThrowable(); + if (throwable != null) { + throw new ExecutionException(throwable); + } + + return response; } @Override public Response execute(String userAgent, Map headers, Verb httpVerb, String completeUrl, File bodyContents) throws InterruptedException, ExecutionException, IOException { - return executeAsync(userAgent, headers, httpVerb, completeUrl, bodyContents, null, - (OAuthRequest.ResponseConverter) null).get(); + final OAuthAsyncRequestThrowableHolderCallback oAuthAsyncRequestThrowableHolderCallback + = new OAuthAsyncRequestThrowableHolderCallback(); + + final Response response = executeAsync(userAgent, headers, httpVerb, completeUrl, bodyContents, + oAuthAsyncRequestThrowableHolderCallback, null).get(); + + final Throwable throwable = oAuthAsyncRequestThrowableHolderCallback.getThrowable(); + if (throwable != null) { + throw new ExecutionException(throwable); + } + + return response; + } + + private class OAuthAsyncRequestThrowableHolderCallback implements OAuthAsyncRequestCallback { + + private Throwable throwable; + + @Override + public void onCompleted(Response response) { + } + + @Override + public void onThrowable(Throwable t) { + throwable = t; + } + + public Throwable getThrowable() { + return throwable; + } } } diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/jdk/JDKHttpFuture.java b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/jdk/JDKHttpFuture.java index 1922d3aef..c4443cc87 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/jdk/JDKHttpFuture.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/jdk/JDKHttpFuture.java @@ -8,6 +8,8 @@ /** * Fake Future. Just to have Future API for the default JDK Http client. It's NOT Async in any way. Just facade.
* That's it. Sync execution with Async methods. This class does NOT provide any async executions. + * + * @param */ public class JDKHttpFuture implements Future { diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/multipart/MultipartPayload.java b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/multipart/MultipartPayload.java index 6f7a5b093..9d60e0caa 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/multipart/MultipartPayload.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/multipart/MultipartPayload.java @@ -72,75 +72,6 @@ private static String parseOrGenerateBoundary(Map headers) { return parsedBoundary == null ? MultipartUtils.generateDefaultBoundary() : parsedBoundary; } - /** - * - * @param fileContent fileContent - * @deprecated use {@link #addBodyPart(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void addFileBodyPart(byte[] fileContent) { - addBodyPart(new FileByteArrayBodyPartPayload(fileContent)); - } - - /** - * - * @param fileContent fileContent - * @param name name - * @deprecated use {@link #addBodyPart(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void addFileBodyPart(byte[] fileContent, String name) { - addBodyPart(new FileByteArrayBodyPartPayload(fileContent, name)); - } - - /** - * - * @param fileContent fileContent - * @param name name - * @param filename filename - * @deprecated use {@link #addBodyPart(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void addFileBodyPart(byte[] fileContent, String name, String filename) { - addBodyPart(new FileByteArrayBodyPartPayload(fileContent, name, filename)); - } - - /** - * - * @param contentType contentType - * @param fileContent fileContent - * @deprecated use {@link #addBodyPart(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void addFileBodyPart(String contentType, byte[] fileContent) { - addBodyPart(new FileByteArrayBodyPartPayload(contentType, fileContent)); - } - - /** - * - * @param contentType contentType - * @param fileContent fileContent - * @param name name - * @deprecated use {@link #addBodyPart(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void addFileBodyPart(String contentType, byte[] fileContent, String name) { - addBodyPart(new FileByteArrayBodyPartPayload(contentType, fileContent, name)); - } - - /** - * - * @param contentType contentType - * @param fileContent fileContent - * @param name name - * @param filename filename - * @deprecated use {@link #addBodyPart(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void addFileBodyPart(String contentType, byte[] fileContent, String name, String filename) { - addBodyPart(new FileByteArrayBodyPartPayload(contentType, fileContent, name, filename)); - } - public void addBodyPart(BodyPartPayload bodyPartPayload) { bodyParts.add(bodyPartPayload); } @@ -153,38 +84,6 @@ public void addBodyPart(MultipartPayload multipartPayload) { bodyParts.add(multipartPayload); } - /** - * - * @param bodyPartPayload bodyPartPayload - * @deprecated use {@link #addBodyPart(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void addBodyPart(byte[] bodyPartPayload) { - addBodyPart(new ByteArrayBodyPartPayload(bodyPartPayload)); - } - - /** - * - * @param bodyPartPayload bodyPartPayload - * @param contentType contentType - * @deprecated use {@link #addBodyPart(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void addBodyPart(byte[] bodyPartPayload, String contentType) { - addBodyPart(new ByteArrayBodyPartPayload(bodyPartPayload, contentType)); - } - - /** - * - * @param bodyPartPayload bodyPartPayload - * @param headers headers - * @deprecated use {@link #addBodyPart(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void addBodyPart(byte[] bodyPartPayload, Map headers) { - addBodyPart(new ByteArrayBodyPartPayload(bodyPartPayload, headers)); - } - public List getBodyParts() { return bodyParts; } diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/java8/Base64.java b/scribejava-core/src/main/java/com/github/scribejava/core/java8/Base64.java deleted file mode 100644 index 221e7f24d..000000000 --- a/scribejava-core/src/main/java/com/github/scribejava/core/java8/Base64.java +++ /dev/null @@ -1,990 +0,0 @@ -/* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.github.scribejava.core.java8; - -import java.io.FilterOutputStream; -import java.io.InputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.Objects; - -/** - * This class consists exclusively of static methods for obtaining encoders and decoders for the Base64 encoding scheme. - * The implementation of this class supports the following types of Base64 as specified in - * RFC 4648 and - * RFC 2045. - * - *
    - *
  • Basic - *

    - * Uses "The Base64 Alphabet" as specified in Table 1 of RFC 4648 and RFC 2045 for encoding and decoding operation. The - * encoder does not add any line feed (line separator) character. The decoder rejects data that contains characters - * outside the base64 alphabet.

  • - * - *
  • URL and Filename safe - *

    - * Uses the "URL and Filename safe Base64 Alphabet" as specified in Table 2 of RFC 4648 for encoding and decoding. The - * encoder does not add any line feed (line separator) character. The decoder rejects data that contains characters - * outside the base64 alphabet.

  • - * - *
  • MIME - *

    - * Uses the "The Base64 Alphabet" as specified in Table 1 of RFC 2045 for encoding and decoding operation. The encoded - * output must be represented in lines of no more than 76 characters each and uses a carriage return {@code '\r'} - * followed immediately by a linefeed {@code '\n'} as the line separator. No line separator is added to the end of the - * encoded output. All line separators or other characters not found in the base64 alphabet table are ignored in - * decoding operation.

  • - *
- * - *

- * Unless otherwise noted, passing a {@code null} argument to a method of this class will cause a {@link java.lang.NullPointerException - * NullPointerException} to be thrown. - * - * @author Xueming Shen - * @since 1.8 - */ -public class Base64 { - - private Base64() { - } - - /** - * Returns a {@link Encoder} that encodes using the - * Basic type base64 encoding scheme. - * - * @return A Base64 encoder. - */ - public static Encoder getEncoder() { - return Encoder.RFC4648; - } - - /** - * Returns a {@link Encoder} that encodes using the - * URL and Filename safe type base64 encoding scheme. - * - * @return A Base64 encoder. - */ - public static Encoder getUrlEncoder() { - return Encoder.RFC4648_URLSAFE; - } - - /** - * Returns a {@link Encoder} that encodes using the - * MIME type base64 encoding scheme. - * - * @return A Base64 encoder. - */ - public static Encoder getMimeEncoder() { - return Encoder.RFC2045; - } - - /** - * Returns a {@link Encoder} that encodes using the - * MIME type base64 encoding scheme with specified line length and line separators. - * - * @param lineLength the length of each output line (rounded down to nearest multiple of 4). If - * {@code lineLength <= 0} the output will not be separated in lines - * @param lineSeparator the line separator for each output line - * - * @return A Base64 encoder. - * - * @throws IllegalArgumentException if {@code lineSeparator} includes any character of "The Base64 Alphabet" as - * specified in Table 1 of RFC 2045. - */ - public static Encoder getMimeEncoder(int lineLength, byte[] lineSeparator) { - Objects.requireNonNull(lineSeparator); - int[] base64 = Decoder.FROM_BASE_64; - for (byte b : lineSeparator) { - if (base64[b & 0xff] != -1) { - throw new IllegalArgumentException( - "Illegal base64 line separator character 0x" + Integer.toString(b, 16)); - } - } - if (lineLength <= 0) { - return Encoder.RFC4648; - } - return new Encoder(false, lineSeparator, lineLength >> 2 << 2, true); - } - - /** - * Returns a {@link Decoder} that decodes using the - * Basic type base64 encoding scheme. - * - * @return A Base64 decoder. - */ - public static Decoder getDecoder() { - return Decoder.RFC4648; - } - - /** - * Returns a {@link Decoder} that decodes using the - * URL and Filename safe type base64 encoding scheme. - * - * @return A Base64 decoder. - */ - public static Decoder getUrlDecoder() { - return Decoder.RFC4648_URLSAFE; - } - - /** - * Returns a {@link Decoder} that decodes using the - * MIME type base64 decoding scheme. - * - * @return A Base64 decoder. - */ - public static Decoder getMimeDecoder() { - return Decoder.RFC2045; - } - - /** - * This class implements an encoder for encoding byte data using the Base64 encoding scheme as specified in RFC 4648 - * and RFC 2045. - * - *

- * Instances of {@link Encoder} class are safe for use by multiple concurrent threads. - * - *

- * Unless otherwise noted, passing a {@code null} argument to a method of this class will cause a - * {@link java.lang.NullPointerException NullPointerException} to be thrown. - * - * @see Decoder - * @since 1.8 - */ - public static class Encoder { - - private final byte[] newline; - private final int linemax; - private final boolean isURL; - private final boolean doPadding; - - /** - * This array is a lookup table that translates 6-bit positive integer index values into their "Base64 Alphabet" - * equivalents as specified in "Table 1: The Base64 Alphabet" of RFC 2045 (and RFC 4648). - */ - private static final char[] TO_BASE_64 = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' - }; - - /** - * It's the lookup table for "URL and Filename safe Base64" as specified in Table 2 of the RFC 4648, with the - * '+' and '/' changed to '-' and '_'. This table is used when BASE64_URL is specified. - */ - private static final char[] TO_BASE_64_URL = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' - }; - - private static final int MIMELINEMAX = 76; - private static final byte[] CRLF = new byte[]{'\r', '\n'}; - - static final Encoder RFC4648 = new Encoder(false, null, -1, true); - static final Encoder RFC4648_URLSAFE = new Encoder(true, null, -1, true); - static final Encoder RFC2045 = new Encoder(false, CRLF, MIMELINEMAX, true); - - private Encoder(boolean isURL, byte[] newline, int linemax, boolean doPadding) { - this.isURL = isURL; - this.newline = newline; - this.linemax = linemax; - this.doPadding = doPadding; - } - - private int outLength(int srclen) { - int len; - if (doPadding) { - len = 4 * ((srclen + 2) / 3); - } else { - int n = srclen % 3; - len = 4 * (srclen / 3) + (n == 0 ? 0 : n + 1); - } - if (linemax > 0) // line separators - { - len += (len - 1) / linemax * newline.length; - } - return len; - } - - /** - * Encodes all bytes from the specified byte array into a newly-allocated byte array using the {@link Base64} - * encoding scheme. The returned byte array is of the length of the resulting bytes. - * - * @param src the byte array to encode - * @return A newly-allocated byte array containing the resulting encoded bytes. - */ - public byte[] encode(byte[] src) { - int len = outLength(src.length); // dst array size - byte[] dst = new byte[len]; - int ret = encode0(src, 0, src.length, dst); - if (ret != dst.length) { - return Arrays.copyOf(dst, ret); - } - return dst; - } - - /** - * Encodes all bytes from the specified byte array using the {@link Base64} encoding scheme, writing the - * resulting bytes to the given output byte array, starting at offset 0. - * - *

- * It is the responsibility of the invoker of this method to make sure the output byte array {@code dst} has - * enough space for encoding all bytes from the input byte array. No bytes will be written to the output byte - * array if the output byte array is not big enough. - * - * @param src the byte array to encode - * @param dst the output byte array - * @return The number of bytes written to the output byte array - * - * @throws IllegalArgumentException if {@code dst} does not have enough space for encoding all input bytes. - */ - public int encode(byte[] src, byte[] dst) { - int len = outLength(src.length); // dst array size - if (dst.length < len) { - throw new IllegalArgumentException( - "Output byte array is too small for encoding all input bytes"); - } - return encode0(src, 0, src.length, dst); - } - - /** - * Encodes the specified byte array into a String using the {@link Base64} encoding scheme. - * - *

- * This method first encodes all input bytes into a base64 encoded byte array and then constructs a new String - * by using the encoded byte array and the {@link java.nio.charset.StandardCharsets#ISO_8859_1 - * ISO-8859-1} charset. - * - *

- * In other words, an invocation of this method has exactly the same effect as invoking - * {@code new String(encode(src), StandardCharsets.ISO_8859_1)}. - * - * @param src the byte array to encode - * @return A String containing the resulting Base64 encoded characters - */ - @SuppressWarnings("deprecation") - public String encodeToString(byte[] src) { - byte[] encoded = encode(src); - return new String(encoded, 0, 0, encoded.length); - } - - /** - * Encodes all remaining bytes from the specified byte buffer into a newly-allocated ByteBuffer using the - * {@link Base64} encoding scheme. - * - * Upon return, the source buffer's position will be updated to its limit; its limit will not have been changed. - * The returned output buffer's position will be zero and its limit will be the number of resulting encoded - * bytes. - * - * @param buffer the source ByteBuffer to encode - * @return A newly-allocated byte buffer containing the encoded bytes. - */ - public ByteBuffer encode(ByteBuffer buffer) { - int len = outLength(buffer.remaining()); - byte[] dst = new byte[len]; - int ret; - if (buffer.hasArray()) { - ret = encode0(buffer.array(), - buffer.arrayOffset() + buffer.position(), - buffer.arrayOffset() + buffer.limit(), - dst); - buffer.position(buffer.limit()); - } else { - byte[] src = new byte[buffer.remaining()]; - buffer.get(src); - ret = encode0(src, 0, src.length, dst); - } - if (ret != dst.length) { - dst = Arrays.copyOf(dst, ret); - } - return ByteBuffer.wrap(dst); - } - - /** - * Wraps an output stream for encoding byte data using the {@link Base64} encoding scheme. - * - *

- * It is recommended to promptly close the returned output stream after use, during which it will flush all - * possible leftover bytes to the underlying output stream. Closing the returned output stream will close the - * underlying output stream. - * - * @param os the output stream. - * @return the output stream for encoding the byte data into the specified Base64 encoded format - */ - public OutputStream wrap(OutputStream os) { - Objects.requireNonNull(os); - return new EncOutputStream(os, isURL ? TO_BASE_64_URL : TO_BASE_64, - newline, linemax, doPadding); - } - - /** - * Returns an encoder instance that encodes equivalently to this one, but without adding any padding character - * at the end of the encoded byte data. - * - *

- * The encoding scheme of this encoder instance is unaffected by this invocation. The returned encoder instance - * should be used for non-padding encoding operation. - * - * @return an equivalent encoder that encodes without adding any padding character at the end - */ - public Encoder withoutPadding() { - if (!doPadding) { - return this; - } - return new Encoder(isURL, newline, linemax, false); - } - - private int encode0(byte[] src, int off, int end, byte[] dst) { - char[] base64 = isURL ? TO_BASE_64_URL : TO_BASE_64; - int sp = off; - int slen = (end - off) / 3 * 3; - int sl = off + slen; - if (linemax > 0 && slen > linemax / 4 * 3) { - slen = linemax / 4 * 3; - } - int dp = 0; - while (sp < sl) { - int sl0 = Math.min(sp + slen, sl); - for (int sp0 = sp, dp0 = dp; sp0 < sl0;) { - int bits = (src[sp0++] & 0xff) << 16 - | (src[sp0++] & 0xff) << 8 - | (src[sp0++] & 0xff); - dst[dp0++] = (byte) base64[(bits >>> 18) & 0x3f]; - dst[dp0++] = (byte) base64[(bits >>> 12) & 0x3f]; - dst[dp0++] = (byte) base64[(bits >>> 6) & 0x3f]; - dst[dp0++] = (byte) base64[bits & 0x3f]; - } - int dlen = (sl0 - sp) / 3 * 4; - dp += dlen; - sp = sl0; - if (dlen == linemax && sp < end) { - for (byte b : newline) { - dst[dp++] = b; - } - } - } - if (sp < end) { // 1 or 2 leftover bytes - int b0 = src[sp++] & 0xff; - dst[dp++] = (byte) base64[b0 >> 2]; - if (sp == end) { - dst[dp++] = (byte) base64[(b0 << 4) & 0x3f]; - if (doPadding) { - dst[dp++] = '='; - dst[dp++] = '='; - } - } else { - int b1 = src[sp++] & 0xff; - dst[dp++] = (byte) base64[(b0 << 4) & 0x3f | (b1 >> 4)]; - dst[dp++] = (byte) base64[(b1 << 2) & 0x3f]; - if (doPadding) { - dst[dp++] = '='; - } - } - } - return dp; - } - } - - /** - * This class implements a decoder for decoding byte data using the Base64 encoding scheme as specified in RFC 4648 - * and RFC 2045. - * - *

- * The Base64 padding character {@code '='} is accepted and interpreted as the end of the encoded byte data, but is - * not required. So if the final unit of the encoded byte data only has two or three Base64 characters (without the - * corresponding padding character(s) padded), they are decoded as if followed by padding character(s). If there is - * a padding character present in the final unit, the correct number of padding character(s) must be present, - * otherwise {@code IllegalArgumentException} ( {@code IOException} when reading from a Base64 stream) is thrown - * during decoding. - * - *

- * Instances of {@link Decoder} class are safe for use by multiple concurrent threads. - * - *

- * Unless otherwise noted, passing a {@code null} argument to a method of this class will cause a - * {@link java.lang.NullPointerException NullPointerException} to be thrown. - * - * @see Encoder - * @since 1.8 - */ - public static class Decoder { - - private final boolean isURL; - private final boolean isMIME; - - /** - * Lookup table for decoding unicode characters drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC - * 2045) into their 6-bit positive integer equivalents. Characters that are not in the Base64 alphabet but fall - * within the bounds of the array are encoded to -1. - * - */ - private static final int[] FROM_BASE_64 = new int[256]; - - static { - Arrays.fill(FROM_BASE_64, -1); - for (int i = 0; i < Encoder.TO_BASE_64.length; i++) { - FROM_BASE_64[Encoder.TO_BASE_64[i]] = i; - } - FROM_BASE_64['='] = -2; - } - - /** - * Lookup table for decoding "URL and Filename safe Base64 Alphabet" as specified in Table2 of the RFC 4648. - */ - private static final int[] FROM_BASE_64_URL = new int[256]; - - static { - Arrays.fill(FROM_BASE_64_URL, -1); - for (int i = 0; i < Encoder.TO_BASE_64_URL.length; i++) { - FROM_BASE_64_URL[Encoder.TO_BASE_64_URL[i]] = i; - } - FROM_BASE_64_URL['='] = -2; - } - - static final Decoder RFC4648 = new Decoder(false, false); - static final Decoder RFC4648_URLSAFE = new Decoder(true, false); - static final Decoder RFC2045 = new Decoder(false, true); - - private Decoder(boolean isURL, boolean isMIME) { - this.isURL = isURL; - this.isMIME = isMIME; - } - - /** - * Decodes all bytes from the input byte array using the {@link Base64} encoding scheme, writing the results - * into a newly-allocated output byte array. The returned byte array is of the length of the resulting bytes. - * - * @param src the byte array to decode - * - * @return A newly-allocated byte array containing the decoded bytes. - * - * @throws IllegalArgumentException if {@code src} is not in valid Base64 scheme - */ - public byte[] decode(byte[] src) { - byte[] dst = new byte[outLength(src, 0, src.length)]; - int ret = decode0(src, 0, src.length, dst); - if (ret != dst.length) { - dst = Arrays.copyOf(dst, ret); - } - return dst; - } - - /** - * Decodes a Base64 encoded String into a newly-allocated byte array using the {@link Base64} encoding scheme. - * - *

- * An invocation of this method has exactly the same effect as invoking - * {@code decode(src.getBytes(StandardCharsets.ISO_8859_1))} - * - * @param src the string to decode - * - * @return A newly-allocated byte array containing the decoded bytes. - * - * @throws IllegalArgumentException if {@code src} is not in valid Base64 scheme - */ - public byte[] decode(String src) { - return decode(src.getBytes(StandardCharsets.ISO_8859_1)); - } - - /** - * Decodes all bytes from the input byte array using the {@link Base64} encoding scheme, writing the results - * into the given output byte array, starting at offset 0. - * - *

- * It is the responsibility of the invoker of this method to make sure the output byte array {@code dst} has - * enough space for decoding all bytes from the input byte array. No bytes will be be written to the output byte - * array if the output byte array is not big enough. - * - *

- * If the input byte array is not in valid Base64 encoding scheme then some bytes may have been written to the - * output byte array before IllegalargumentException is thrown. - * - * @param src the byte array to decode - * @param dst the output byte array - * - * @return The number of bytes written to the output byte array - * - * @throws IllegalArgumentException if {@code src} is not in valid Base64 scheme, or {@code dst} does not have - * enough space for decoding all input bytes. - */ - public int decode(byte[] src, byte[] dst) { - int len = outLength(src, 0, src.length); - if (dst.length < len) { - throw new IllegalArgumentException( - "Output byte array is too small for decoding all input bytes"); - } - return decode0(src, 0, src.length, dst); - } - - /** - * Decodes all bytes from the input byte buffer using the {@link Base64} encoding scheme, writing the results - * into a newly-allocated ByteBuffer. - * - *

- * Upon return, the source buffer's position will be updated to its limit; its limit will not have been changed. - * The returned output buffer's position will be zero and its limit will be the number of resulting decoded - * bytes - * - *

- * {@code IllegalArgumentException} is thrown if the input buffer is not in valid Base64 encoding scheme. The - * position of the input buffer will not be advanced in this case. - * - * @param buffer the ByteBuffer to decode - * - * @return A newly-allocated byte buffer containing the decoded bytes - * - * @throws IllegalArgumentException if {@code src} is not in valid Base64 scheme. - */ - public ByteBuffer decode(ByteBuffer buffer) { - int pos0 = buffer.position(); - try { - byte[] src; - int sp; - int sl; - if (buffer.hasArray()) { - src = buffer.array(); - sp = buffer.arrayOffset() + buffer.position(); - sl = buffer.arrayOffset() + buffer.limit(); - buffer.position(buffer.limit()); - } else { - src = new byte[buffer.remaining()]; - buffer.get(src); - sp = 0; - sl = src.length; - } - byte[] dst = new byte[outLength(src, sp, sl)]; - return ByteBuffer.wrap(dst, 0, decode0(src, sp, sl, dst)); - } catch (IllegalArgumentException iae) { - buffer.position(pos0); - throw iae; - } - } - - /** - * Returns an input stream for decoding {@link Base64} encoded byte stream. - * - *

- * The {@code read} methods of the returned {@code InputStream} will throw {@code IOException} when reading - * bytes that cannot be decoded. - * - *

- * Closing the returned input stream will close the underlying input stream. - * - * @param is the input stream - * - * @return the input stream for decoding the specified Base64 encoded byte stream - */ - public InputStream wrap(InputStream is) { - Objects.requireNonNull(is); - return new DecInputStream(is, isURL ? FROM_BASE_64_URL : FROM_BASE_64, isMIME); - } - - private int outLength(byte[] src, int sp, int sl) { - int[] base64 = isURL ? FROM_BASE_64_URL : FROM_BASE_64; - int paddings = 0; - int len = sl - sp; - if (len == 0) { - return 0; - } - if (len < 2) { - if (isMIME && base64[0] == -1) { - return 0; - } - throw new IllegalArgumentException( - "Input byte[] should at least have 2 bytes for base64 bytes"); - } - if (isMIME) { - // scan all bytes to fill out all non-alphabet. a performance - // trade-off of pre-scan or Arrays.copyOf - int n = 0; - while (sp < sl) { - int b = src[sp++] & 0xff; - if (b == '=') { - len -= sl - sp + 1; - break; - } - b = base64[b]; - if (b == -1) { - n++; - } - } - len -= n; - } else { - if (src[sl - 1] == '=') { - paddings++; - if (src[sl - 2] == '=') { - paddings++; - } - } - } - if (paddings == 0 && (len & 0x3) != 0) { - paddings = 4 - (len & 0x3); - } - return 3 * ((len + 3) / 4) - paddings; - } - - private int decode0(byte[] src, int sp, int sl, byte[] dst) { - int[] base64 = isURL ? FROM_BASE_64_URL : FROM_BASE_64; - int dp = 0; - int bits = 0; - int shiftto = 18; // pos of first byte of 4-byte atom - while (sp < sl) { - int b = src[sp++] & 0xff; - b = base64[b]; - if (b < 0) { - if (b == -2) { // padding byte '=' - // = shiftto==18 unnecessary padding - // x= shiftto==12 a dangling single x - // x to be handled together with non-padding case - // xx= shiftto==6&&sp==sl missing last = - // xx=y shiftto==6 last is not = - if (shiftto == 6 && (sp == sl || src[sp++] != '=') - || shiftto == 18) { - throw new IllegalArgumentException( - "Input byte array has wrong 4-byte ending unit"); - } - break; - } - if (isMIME) // skip if for rfc2045 - { - continue; - } else { - throw new IllegalArgumentException( - "Illegal base64 character " - + Integer.toString(src[sp - 1], 16)); - } - } - bits |= b << shiftto; - shiftto -= 6; - if (shiftto < 0) { - dst[dp++] = (byte) (bits >> 16); - dst[dp++] = (byte) (bits >> 8); - dst[dp++] = (byte) (bits); - shiftto = 18; - bits = 0; - } - } - // reached end of byte array or hit padding '=' characters. - switch (shiftto) { - case 6: - dst[dp++] = (byte) (bits >> 16); - break; - case 0: - dst[dp++] = (byte) (bits >> 16); - dst[dp++] = (byte) (bits >> 8); - break; - case 12: - // dangling single "x", incorrectly encoded. - throw new IllegalArgumentException( - "Last unit does not have enough valid bits"); - default: - break; - } - // anything left is invalid, if is not MIME. - // if MIME, ignore all non-base64 character - while (sp < sl) { - if (isMIME && base64[src[sp++]] < 0) { - continue; - } - throw new IllegalArgumentException( - "Input byte array has incorrect ending byte at " + sp); - } - return dp; - } - } - - /* - * An output stream for encoding bytes into the Base64. - */ - private static class EncOutputStream extends FilterOutputStream { - - private int leftover; - private int b0; - private int b1; - private int b2; - private boolean closed; - - private final char[] base64; // byte->base64 mapping - private final byte[] newline; // line separator, if needed - private final int linemax; - private final boolean doPadding;// whether or not to pad - private int linepos; - - EncOutputStream(OutputStream os, char[] base64, - byte[] newline, int linemax, boolean doPadding) { - super(os); - this.base64 = base64; - this.newline = newline; - this.linemax = linemax; - this.doPadding = doPadding; - } - - @Override - public void write(int b) throws IOException { - byte[] buf = new byte[1]; - buf[0] = (byte) (b & 0xff); - write(buf, 0, 1); - } - - private void checkNewline() throws IOException { - if (linepos == linemax) { - out.write(newline); - linepos = 0; - } - } - - @Override - public void write(byte[] b, int off, int len) throws IOException { - if (closed) { - throw new IOException("Stream is closed"); - } - if (off < 0 || len < 0 || off + len > b.length) { - throw new ArrayIndexOutOfBoundsException(); - } - if (len == 0) { - return; - } - if (leftover != 0) { - if (leftover == 1) { - b1 = b[off++] & 0xff; - len--; - if (len == 0) { - leftover++; - return; - } - } - b2 = b[off++] & 0xff; - len--; - checkNewline(); - out.write(base64[b0 >> 2]); - out.write(base64[(b0 << 4) & 0x3f | (b1 >> 4)]); - out.write(base64[(b1 << 2) & 0x3f | (b2 >> 6)]); - out.write(base64[b2 & 0x3f]); - linepos += 4; - } - int nBits24 = len / 3; - leftover = len - (nBits24 * 3); - while (nBits24-- > 0) { - checkNewline(); - int bits = (b[off++] & 0xff) << 16 - | (b[off++] & 0xff) << 8 - | (b[off++] & 0xff); - out.write(base64[(bits >>> 18) & 0x3f]); - out.write(base64[(bits >>> 12) & 0x3f]); - out.write(base64[(bits >>> 6) & 0x3f]); - out.write(base64[bits & 0x3f]); - linepos += 4; - } - if (leftover == 1) { - b0 = b[off++] & 0xff; - } else if (leftover == 2) { - b0 = b[off++] & 0xff; - b1 = b[off++] & 0xff; - } - } - - @Override - public void close() throws IOException { - if (!closed) { - closed = true; - if (leftover == 1) { - checkNewline(); - out.write(base64[b0 >> 2]); - out.write(base64[(b0 << 4) & 0x3f]); - if (doPadding) { - out.write('='); - out.write('='); - } - } else if (leftover == 2) { - checkNewline(); - out.write(base64[b0 >> 2]); - out.write(base64[(b0 << 4) & 0x3f | (b1 >> 4)]); - out.write(base64[(b1 << 2) & 0x3f]); - if (doPadding) { - out.write('='); - } - } - leftover = 0; - out.close(); - } - } - } - - /* - * An input stream for decoding Base64 bytes - */ - private static class DecInputStream extends InputStream { - - private final InputStream is; - private final boolean isMIME; - private final int[] base64; // base64 -> byte mapping - private int bits; // 24-bit buffer for decoding - private int nextin = 18; // next available "off" in "bits" for input; - // -> 18, 12, 6, 0 - private int nextout = -8; // next available "off" in "bits" for output; - // -> 8, 0, -8 (no byte for output) - private boolean eof; - private boolean closed; - - private final byte[] sbBuf = new byte[1]; - - DecInputStream(InputStream is, int[] base64, boolean isMIME) { - this.is = is; - this.base64 = base64; - this.isMIME = isMIME; - } - - @Override - public int read() throws IOException { - return read(sbBuf, 0, 1) == -1 ? -1 : sbBuf[0] & 0xff; - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - if (closed) { - throw new IOException("Stream is closed"); - } - if (eof && nextout < 0) // eof and no leftover - { - return -1; - } - if (off < 0 || len < 0 || len > b.length - off) { - throw new IndexOutOfBoundsException(); - } - int oldOff = off; - if (nextout >= 0) { // leftover output byte(s) in bits buf - do { - if (len == 0) { - return off - oldOff; - } - b[off++] = (byte) (bits >> nextout); - len--; - nextout -= 8; - } while (nextout >= 0); - bits = 0; - } - while (len > 0) { - int v = is.read(); - if (v == -1) { - eof = true; - if (nextin != 18) { - if (nextin == 12) { - throw new IOException("Base64 stream has one un-decoded dangling byte."); - } - // treat ending xx/xxx without padding character legal. - // same logic as v == '=' below - b[off++] = (byte) (bits >> (16)); - len--; - if (nextin == 0) { // only one padding byte - if (len == 0) { // no enough output space - bits >>= 8; // shift to lowest byte - nextout = 0; - } else { - b[off++] = (byte) (bits >> 8); - } - } - } - if (off == oldOff) { - return -1; - } else { - return off - oldOff; - } - } - if (v == '=') { // padding byte(s) - // = shiftto==18 unnecessary padding - // x= shiftto==12 dangling x, invalid unit - // xx= shiftto==6 && missing last '=' - // xx=y or last is not '=' - if (nextin == 18 || nextin == 12 - || nextin == 6 && is.read() != '=') { - throw new IOException("Illegal base64 ending sequence:" + nextin); - } - b[off++] = (byte) (bits >> (16)); - len--; - if (nextin == 0) { // only one padding byte - if (len == 0) { // no enough output space - bits >>= 8; // shift to lowest byte - nextout = 0; - } else { - b[off++] = (byte) (bits >> 8); - } - } - eof = true; - break; - } - v = base64[v]; - if (v == -1) { - if (isMIME) // skip if for rfc2045 - { - continue; - } else { - throw new IOException("Illegal base64 character " - + Integer.toString(v, 16)); - } - } - bits |= v << nextin; - if (nextin == 0) { - nextin = 18; // clear for next - nextout = 16; - while (nextout >= 0) { - b[off++] = (byte) (bits >> nextout); - len--; - nextout -= 8; - if (len == 0 && nextout >= 0) { // don't clean "bits" - return off - oldOff; - } - } - bits = 0; - } else { - nextin -= 6; - } - } - return off - oldOff; - } - - @Override - public int available() throws IOException { - if (closed) { - throw new IOException("Stream is closed"); - } - return is.available(); // TBD: - } - - @Override - public void close() throws IOException { - if (!closed) { - closed = true; - is.close(); - } - } - } -} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/java8/Consumer.java b/scribejava-core/src/main/java/com/github/scribejava/core/java8/Consumer.java deleted file mode 100644 index 83306b7be..000000000 --- a/scribejava-core/src/main/java/com/github/scribejava/core/java8/Consumer.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.github.scribejava.core.java8; - -/** - * Represents an operation that accepts a single input argument and returns no result. Unlike most other functional - * interfaces, {@code Consumer} is expected to operate via side-effects. - * - *

- * This is a functional interface - * whose functional method is {@link #accept(Object)}. - * - * @param the type of the input to the operation - * - * @since 1.8 - */ -public interface Consumer { - - /** - * Performs this operation on the given argument. - * - * @param t the input argument - */ - void accept(T t); -} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuth2AccessToken.java b/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuth2AccessToken.java index 00d5b1636..f02bc57c6 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuth2AccessToken.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuth2AccessToken.java @@ -5,11 +5,9 @@ /** * Represents an OAuth 2 Access token. - *

* http://tools.ietf.org/html/rfc6749#section-5.1 * * @see OAuth 2 Access Token Specification - *

*/ public class OAuth2AccessToken extends Token { diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuth2AccessTokenErrorResponse.java b/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuth2AccessTokenErrorResponse.java index f36572e0a..5c4a65a19 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuth2AccessTokenErrorResponse.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuth2AccessTokenErrorResponse.java @@ -1,29 +1,27 @@ package com.github.scribejava.core.model; -import com.github.scribejava.core.exceptions.OAuthException; import com.github.scribejava.core.oauth2.OAuth2Error; +import java.io.IOException; import java.net.URI; /** * Representing "5.2. Error Response" */ -public class OAuth2AccessTokenErrorResponse extends OAuthException { +public class OAuth2AccessTokenErrorResponse extends OAuthResponseException { private static final long serialVersionUID = 2309424849700276816L; private final OAuth2Error error; private final String errorDescription; private final URI errorUri; - private final String rawResponse; public OAuth2AccessTokenErrorResponse(OAuth2Error error, String errorDescription, URI errorUri, - String rawResponse) { + Response rawResponse) throws IOException { super(rawResponse); this.error = error; this.errorDescription = errorDescription; this.errorUri = errorUri; - this.rawResponse = rawResponse; } public OAuth2Error getError() { @@ -38,7 +36,4 @@ public URI getErrorUri() { return errorUri; } - public String getRawResponse() { - return rawResponse; - } } diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuthRequest.java b/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuthRequest.java index 2194545fa..ccf26157c 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuthRequest.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuthRequest.java @@ -2,7 +2,6 @@ import com.github.scribejava.core.exceptions.OAuthException; import com.github.scribejava.core.httpclient.multipart.BodyPartPayload; -import com.github.scribejava.core.httpclient.multipart.FileByteArrayBodyPartPayload; import com.github.scribejava.core.httpclient.multipart.MultipartPayload; import java.io.File; import java.io.IOException; @@ -161,264 +160,11 @@ public void initMultipartPayload(String subtype, String boundary, Map headers) { - initMultipartPayload(); - addByteArrayBodyPartPayloadInMultipartPayload(bodyPartPayload, headers); - } - - /** - * - * @param bodyPartPayload bodyPartPayload - * @deprecated use - * {@link #addBodyPartPayloadInMultipartPayload(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void addByteArrayBodyPartPayloadInMultipartPayload(byte[] bodyPartPayload) { - multipartPayload.addBodyPart(bodyPartPayload); - } - - /** - * - * @param bodyPartPayload bodyPartPayload - * @param contentType contentType - * @deprecated use - * {@link #addBodyPartPayloadInMultipartPayload(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void addByteArrayBodyPartPayloadInMultipartPayload(byte[] bodyPartPayload, String contentType) { - multipartPayload.addBodyPart(bodyPartPayload, contentType); - } - - /** - * - * @param bodyPartPayload bodyPartPayload - * @param headers headers - * @deprecated use - * {@link #addBodyPartPayloadInMultipartPayload(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void addByteArrayBodyPartPayloadInMultipartPayload(byte[] bodyPartPayload, Map headers) { - multipartPayload.addBodyPart(bodyPartPayload, headers); - } - - /** - * - * @param fileContent fileContent - * @deprecated use - * {@link #setBodyPartPayloadInMultipartPayload(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void setFileByteArrayBodyPartPayloadInMultipartPayload(byte[] fileContent) { - initMultipartPayload(); - addFileByteArrayBodyPartPayloadInMultipartPayload(fileContent); - } - - /** - * - * @param contentType contentType - * @param fileContent fileContent - * @deprecated use - * {@link #setBodyPartPayloadInMultipartPayload(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void setFileByteArrayBodyPartPayloadInMultipartPayload(String contentType, byte[] fileContent) { - initMultipartPayload(); - addFileByteArrayBodyPartPayloadInMultipartPayload(contentType, fileContent); - } - - /** - * - * @param fileContent fileContent - * @param name name - * @deprecated use - * {@link #setBodyPartPayloadInMultipartPayload(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void setFileByteArrayBodyPartPayloadInMultipartPayload(byte[] fileContent, String name) { - initMultipartPayload(); - addFileByteArrayBodyPartPayloadInMultipartPayload(fileContent, name); - } - - /** - * - * @param contentType contentType - * @param fileContent fileContent - * @param name name - * @deprecated use - * {@link #setBodyPartPayloadInMultipartPayload(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void setFileByteArrayBodyPartPayloadInMultipartPayload(String contentType, byte[] fileContent, String name) { - initMultipartPayload(); - addFileByteArrayBodyPartPayloadInMultipartPayload(contentType, fileContent, name); - } - - /** - * - * @param fileContent fileContent - * @param name name - * @param filename filename - * @deprecated use - * {@link #setBodyPartPayloadInMultipartPayload(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void setFileByteArrayBodyPartPayloadInMultipartPayload(byte[] fileContent, String name, String filename) { - initMultipartPayload(); - addFileByteArrayBodyPartPayloadInMultipartPayload(fileContent, name, filename); - } - - /** - * - * @param contentType contentType - * @param fileContent fileContent - * @param name name - * @param filename filename - * @deprecated use - * {@link #setBodyPartPayloadInMultipartPayload(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void setFileByteArrayBodyPartPayloadInMultipartPayload(String contentType, byte[] fileContent, String name, - String filename) { - initMultipartPayload(); - addFileByteArrayBodyPartPayloadInMultipartPayload(contentType, fileContent, name, filename); - } - - /** - * - * @param fileByteArrayBodyPartPayload fileByteArrayBodyPartPayload - * @deprecated use - * {@link #setBodyPartPayloadInMultipartPayload(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void setFileByteArrayBodyPartPayloadInMultipartPayload( - FileByteArrayBodyPartPayload fileByteArrayBodyPartPayload) { - setBodyPartPayloadInMultipartPayload(fileByteArrayBodyPartPayload); - } - public void setBodyPartPayloadInMultipartPayload(BodyPartPayload bodyPartPayload) { initMultipartPayload(); addBodyPartPayloadInMultipartPayload(bodyPartPayload); } - /** - * - * @param fileContent fileContent - * @deprecated use - * {@link #addBodyPartPayloadInMultipartPayload(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void addFileByteArrayBodyPartPayloadInMultipartPayload(byte[] fileContent) { - multipartPayload.addFileBodyPart(fileContent); - } - - /** - * - * @param contentType contentType - * @param fileContent fileContent - * @deprecated use - * {@link #addBodyPartPayloadInMultipartPayload(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void addFileByteArrayBodyPartPayloadInMultipartPayload(String contentType, byte[] fileContent) { - multipartPayload.addFileBodyPart(contentType, fileContent); - } - - /** - * - * @param fileContent fileContent - * @param name name - * @deprecated use - * {@link #addBodyPartPayloadInMultipartPayload(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void addFileByteArrayBodyPartPayloadInMultipartPayload(byte[] fileContent, String name) { - multipartPayload.addFileBodyPart(fileContent, name); - } - - /** - * @param contentType contentType - * @param fileContent fileContent - * @param name name - * @deprecated use - * {@link #addBodyPartPayloadInMultipartPayload(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void addFileByteArrayBodyPartPayloadInMultipartPayload(String contentType, byte[] fileContent, String name) { - multipartPayload.addFileBodyPart(contentType, fileContent, name); - } - - /** - * - * @param fileContent fileContent - * @param name name - * @param filename filename - * @deprecated use - * {@link #addBodyPartPayloadInMultipartPayload(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void addFileByteArrayBodyPartPayloadInMultipartPayload(byte[] fileContent, String name, String filename) { - multipartPayload.addFileBodyPart(fileContent, name, filename); - } - - /** - * @param contentType contentType - * @param fileContent fileContent - * @param name name - * @param filename filename - * @deprecated use - * {@link #addBodyPartPayloadInMultipartPayload(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void addFileByteArrayBodyPartPayloadInMultipartPayload(String contentType, byte[] fileContent, String name, - String filename) { - multipartPayload.addFileBodyPart(contentType, fileContent, name, filename); - } - - /** - * - * @param fileByteArrayBodyPartPayload fileByteArrayBodyPartPayload - * @deprecated use - * {@link #addBodyPartPayloadInMultipartPayload(com.github.scribejava.core.httpclient.multipart.BodyPartPayload)} - */ - @Deprecated - public void addFileByteArrayBodyPartPayloadInMultipartPayload( - FileByteArrayBodyPartPayload fileByteArrayBodyPartPayload) { - multipartPayload.addBodyPart(fileByteArrayBodyPartPayload); - } - public void addBodyPartPayloadInMultipartPayload(BodyPartPayload bodyPartPayload) { multipartPayload.addBodyPart(bodyPartPayload); } diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuthResponseException.java b/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuthResponseException.java new file mode 100644 index 000000000..5b6da25cf --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuthResponseException.java @@ -0,0 +1,44 @@ +package com.github.scribejava.core.model; + +import com.github.scribejava.core.exceptions.OAuthException; +import java.io.IOException; +import java.util.Objects; + +public class OAuthResponseException extends OAuthException { + + private static final long serialVersionUID = 1309424849700276816L; + + private final transient Response response; + + public OAuthResponseException(Response rawResponse) throws IOException { + super(rawResponse.getBody()); + this.response = rawResponse; + } + + public Response getResponse() { + return response; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 29 * hash + Objects.hashCode(response); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final OAuthResponseException other = (OAuthResponseException) obj; + return Objects.equals(this.response, other.response); + } + +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/oauth/AccessTokenRequestParams.java b/scribejava-core/src/main/java/com/github/scribejava/core/oauth/AccessTokenRequestParams.java index 787ead8a7..d45377a56 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/oauth/AccessTokenRequestParams.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/oauth/AccessTokenRequestParams.java @@ -1,9 +1,18 @@ package com.github.scribejava.core.oauth; +import com.github.scribejava.core.builder.ScopeBuilder; +import java.util.HashMap; +import java.util.Map; + +/** + * not thread safe + */ public class AccessTokenRequestParams { + private final String code; private String pkceCodeVerifier; private String scope; + private Map extraParameters; public AccessTokenRequestParams(String code) { this.code = code; @@ -23,6 +32,39 @@ public AccessTokenRequestParams scope(String scope) { return this; } + public AccessTokenRequestParams scope(ScopeBuilder scope) { + this.scope = scope.build(); + return this; + } + + public AccessTokenRequestParams addExtraParameters(Map extraParameters) { + if (extraParameters == null || extraParameters.isEmpty()) { + return this; + } + if (this.extraParameters == null) { + extraParameters = new HashMap<>(); + } + this.extraParameters.putAll(extraParameters); + return this; + } + + public AccessTokenRequestParams addExtraParameter(String name, String value) { + if (this.extraParameters == null) { + extraParameters = new HashMap<>(); + } + this.extraParameters.put(name, value); + return this; + } + + public AccessTokenRequestParams setExtraParameters(Map extraParameters) { + this.extraParameters = extraParameters; + return this; + } + + public Map getExtraParameters() { + return extraParameters; + } + public String getCode() { return code; } diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/oauth/OAuth10aService.java b/scribejava-core/src/main/java/com/github/scribejava/core/oauth/OAuth10aService.java index 89fd279b1..f48308b2e 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/oauth/OAuth10aService.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/oauth/OAuth10aService.java @@ -39,7 +39,7 @@ public OAuth1RequestToken getRequestToken() throws IOException, InterruptedExcep final OAuthRequest request = prepareRequestTokenRequest(); log("sending request..."); - try (Response response = execute(request)) { + try ( Response response = execute(request)) { if (isDebug()) { final String body = response.getBody(); log("response status code: %s", response.getCode()); @@ -105,7 +105,7 @@ public OAuth1AccessToken getAccessToken(OAuth1RequestToken requestToken, String log("obtaining access token from %s", api.getAccessTokenEndpoint()); } final OAuthRequest request = prepareAccessTokenRequest(requestToken, oauthVerifier); - try (Response response = execute(request)) { + try ( Response response = execute(request)) { return api.getAccessTokenExtractor().extract(response); } } diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/oauth/OAuth20Service.java b/scribejava-core/src/main/java/com/github/scribejava/core/oauth/OAuth20Service.java index 9b30fb9b5..82052e351 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/oauth/OAuth20Service.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/oauth/OAuth20Service.java @@ -40,6 +40,108 @@ public OAuth20Service(DefaultApi20 api, String apiKey, String apiSecret, String this.defaultScope = defaultScope; } + // ===== common OAuth methods ===== + /** + * {@inheritDoc} + */ + @Override + public String getVersion() { + return VERSION; + } + + public void signRequest(String accessToken, OAuthRequest request) { + api.getBearerSignature().signRequest(accessToken, request); + } + + public void signRequest(OAuth2AccessToken accessToken, OAuthRequest request) { + signRequest(accessToken == null ? null : accessToken.getAccessToken(), request); + } + + /** + * Returns the URL where you should redirect your users to authenticate your application. + * + * @return the URL where you should redirect your users + */ + public String getAuthorizationUrl() { + return createAuthorizationUrlBuilder().build(); + } + + public String getAuthorizationUrl(String state) { + return createAuthorizationUrlBuilder() + .state(state) + .build(); + } + + /** + * Returns the URL where you should redirect your users to authenticate your application. + * + * @param additionalParams any additional GET params to add to the URL + * @return the URL where you should redirect your users + */ + public String getAuthorizationUrl(Map additionalParams) { + return createAuthorizationUrlBuilder() + .additionalParams(additionalParams) + .build(); + } + + public String getAuthorizationUrl(PKCE pkce) { + return createAuthorizationUrlBuilder() + .pkce(pkce) + .build(); + } + + public AuthorizationUrlBuilder createAuthorizationUrlBuilder() { + return new AuthorizationUrlBuilder(this); + } + + public DefaultApi20 getApi() { + return api; + } + + public OAuth2Authorization extractAuthorization(String redirectLocation) { + final OAuth2Authorization authorization = new OAuth2Authorization(); + int end = redirectLocation.indexOf('#'); + if (end == -1) { + end = redirectLocation.length(); + } + for (String param : redirectLocation.substring(redirectLocation.indexOf('?') + 1, end).split("&")) { + final String[] keyValue = param.split("="); + if (keyValue.length == 2) { + try { + switch (keyValue[0]) { + case "code": + authorization.setCode(URLDecoder.decode(keyValue[1], "UTF-8")); + break; + case "state": + authorization.setState(URLDecoder.decode(keyValue[1], "UTF-8")); + break; + default: //just ignore any other param; + } + } catch (UnsupportedEncodingException ueE) { + throw new IllegalStateException("jvm without UTF-8, really?", ueE); + } + } + } + return authorization; + } + + public String getResponseType() { + return responseType; + } + + public String getDefaultScope() { + return defaultScope; + } + + protected void logRequestWithParams(String requestDescription, OAuthRequest request) { + if (isDebug()) { + log("created " + requestDescription + " request with body params [%s], query string params [%s]", + request.getBodyParams().asFormUrlEncodedString(), + request.getQueryStringParams().asFormUrlEncodedString()); + } + } + + // ===== common AccessToken request methods ===== //protected to facilitate mocking protected OAuth2AccessToken sendAccessTokenRequestSync(OAuthRequest request) throws IOException, InterruptedException, ExecutionException { @@ -83,6 +185,41 @@ public OAuth2AccessToken convert(Response response) throws IOException { }); } + // ===== get AccessToken authorisation code flow methods ===== + protected OAuthRequest createAccessTokenRequest(AccessTokenRequestParams params) { + final OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint()); + + api.getClientAuthentication().addClientAuthentication(request, getApiKey(), getApiSecret()); + + request.addParameter(OAuthConstants.CODE, params.getCode()); + final String callback = getCallback(); + if (callback != null) { + request.addParameter(OAuthConstants.REDIRECT_URI, callback); + } + final String scope = params.getScope(); + if (scope != null) { + request.addParameter(OAuthConstants.SCOPE, scope); + } else if (defaultScope != null) { + request.addParameter(OAuthConstants.SCOPE, defaultScope); + } + request.addParameter(OAuthConstants.GRANT_TYPE, OAuthConstants.AUTHORIZATION_CODE); + + final String pkceCodeVerifier = params.getPkceCodeVerifier(); + if (pkceCodeVerifier != null) { + request.addParameter(PKCE.PKCE_CODE_VERIFIER_PARAM, pkceCodeVerifier); + } + + final Map extraParameters = params.getExtraParameters(); + if (extraParameters != null && !extraParameters.isEmpty()) { + for (Map.Entry extraParameter : extraParameters.entrySet()) { + request.addParameter(extraParameter.getKey(), extraParameter.getValue()); + } + } + + logRequestWithParams("access token", request); + return request; + } + public Future getAccessTokenAsync(String code) { return getAccessToken(AccessTokenRequestParams.create(code), null); } @@ -118,29 +255,26 @@ public Future getAccessToken(String code, return getAccessToken(AccessTokenRequestParams.create(code), callback); } - protected OAuthRequest createAccessTokenRequest(AccessTokenRequestParams params) { - final OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint()); + // ===== refresh AccessToken methods ===== + protected OAuthRequest createRefreshTokenRequest(String refreshToken, String scope) { + if (refreshToken == null || refreshToken.isEmpty()) { + throw new IllegalArgumentException("The refreshToken cannot be null or empty"); + } + final OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getRefreshTokenEndpoint()); api.getClientAuthentication().addClientAuthentication(request, getApiKey(), getApiSecret()); - request.addParameter(OAuthConstants.CODE, params.getCode()); - final String callback = getCallback(); - if (callback != null) { - request.addParameter(OAuthConstants.REDIRECT_URI, callback); - } - final String scope = params.getScope(); if (scope != null) { request.addParameter(OAuthConstants.SCOPE, scope); } else if (defaultScope != null) { request.addParameter(OAuthConstants.SCOPE, defaultScope); } - request.addParameter(OAuthConstants.GRANT_TYPE, OAuthConstants.AUTHORIZATION_CODE); - final String pkceCodeVerifier = params.getPkceCodeVerifier(); - if (pkceCodeVerifier != null) { - request.addParameter(PKCE.PKCE_CODE_VERIFIER_PARAM, pkceCodeVerifier); - } - logRequestWithParams("access token", request); + request.addParameter(OAuthConstants.REFRESH_TOKEN, refreshToken); + request.addParameter(OAuthConstants.GRANT_TYPE, OAuthConstants.REFRESH_TOKEN); + + logRequestWithParams("refresh token", request); + return request; } @@ -178,13 +312,11 @@ public Future refreshAccessToken(String refreshToken, String return sendAccessTokenRequestAsync(request, callback); } - protected OAuthRequest createRefreshTokenRequest(String refreshToken, String scope) { - if (refreshToken == null || refreshToken.isEmpty()) { - throw new IllegalArgumentException("The refreshToken cannot be null or empty"); - } - final OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getRefreshTokenEndpoint()); - - api.getClientAuthentication().addClientAuthentication(request, getApiKey(), getApiSecret()); + // ===== get AccessToken password grant flow methods ===== + protected OAuthRequest createAccessTokenPasswordGrantRequest(String username, String password, String scope) { + final OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint()); + request.addParameter(OAuthConstants.USERNAME, username); + request.addParameter(OAuthConstants.PASSWORD, password); if (scope != null) { request.addParameter(OAuthConstants.SCOPE, scope); @@ -192,10 +324,11 @@ protected OAuthRequest createRefreshTokenRequest(String refreshToken, String sco request.addParameter(OAuthConstants.SCOPE, defaultScope); } - request.addParameter(OAuthConstants.REFRESH_TOKEN, refreshToken); - request.addParameter(OAuthConstants.GRANT_TYPE, OAuthConstants.REFRESH_TOKEN); + request.addParameter(OAuthConstants.GRANT_TYPE, OAuthConstants.PASSWORD); - logRequestWithParams("refresh token", request); + api.getClientAuthentication().addClientAuthentication(request, getApiKey(), getApiSecret()); + + logRequestWithParams("access token password grant", request); return request; } @@ -245,22 +378,20 @@ public Future getAccessTokenPasswordGrantAsync(String usernam return sendAccessTokenRequestAsync(request, callback); } - protected OAuthRequest createAccessTokenPasswordGrantRequest(String username, String password, String scope) { + // ===== get AccessToken client credentials flow methods ===== + protected OAuthRequest createAccessTokenClientCredentialsGrantRequest(String scope) { final OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint()); - request.addParameter(OAuthConstants.USERNAME, username); - request.addParameter(OAuthConstants.PASSWORD, password); + + api.getClientAuthentication().addClientAuthentication(request, getApiKey(), getApiSecret()); if (scope != null) { request.addParameter(OAuthConstants.SCOPE, scope); } else if (defaultScope != null) { request.addParameter(OAuthConstants.SCOPE, defaultScope); } + request.addParameter(OAuthConstants.GRANT_TYPE, OAuthConstants.CLIENT_CREDENTIALS); - request.addParameter(OAuthConstants.GRANT_TYPE, OAuthConstants.PASSWORD); - - api.getClientAuthentication().addClientAuthentication(request, getApiKey(), getApiSecret()); - - logRequestWithParams("access token password grant", request); + logRequestWithParams("access token client credentials grant", request); return request; } @@ -308,80 +439,7 @@ public Future getAccessTokenClientCredentialsGrant(String sco return sendAccessTokenRequestAsync(request, callback); } - protected OAuthRequest createAccessTokenClientCredentialsGrantRequest(String scope) { - final OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint()); - - api.getClientAuthentication().addClientAuthentication(request, getApiKey(), getApiSecret()); - - if (scope != null) { - request.addParameter(OAuthConstants.SCOPE, scope); - } else if (defaultScope != null) { - request.addParameter(OAuthConstants.SCOPE, defaultScope); - } - request.addParameter(OAuthConstants.GRANT_TYPE, OAuthConstants.CLIENT_CREDENTIALS); - - logRequestWithParams("access token client credentials grant", request); - - return request; - } - - /** - * {@inheritDoc} - */ - @Override - public String getVersion() { - return VERSION; - } - - public void signRequest(String accessToken, OAuthRequest request) { - api.getBearerSignature().signRequest(accessToken, request); - } - - public void signRequest(OAuth2AccessToken accessToken, OAuthRequest request) { - signRequest(accessToken == null ? null : accessToken.getAccessToken(), request); - } - - /** - * Returns the URL where you should redirect your users to authenticate your application. - * - * @return the URL where you should redirect your users - */ - public String getAuthorizationUrl() { - return createAuthorizationUrlBuilder().build(); - } - - public String getAuthorizationUrl(String state) { - return createAuthorizationUrlBuilder() - .state(state) - .build(); - } - - /** - * Returns the URL where you should redirect your users to authenticate your application. - * - * @param additionalParams any additional GET params to add to the URL - * @return the URL where you should redirect your users - */ - public String getAuthorizationUrl(Map additionalParams) { - return createAuthorizationUrlBuilder() - .additionalParams(additionalParams) - .build(); - } - - public String getAuthorizationUrl(PKCE pkce) { - return createAuthorizationUrlBuilder() - .pkce(pkce) - .build(); - } - - public AuthorizationUrlBuilder createAuthorizationUrlBuilder() { - return new AuthorizationUrlBuilder(this); - } - - public DefaultApi20 getApi() { - return api; - } - + // ===== revoke AccessToken methods ===== protected OAuthRequest createRevokeTokenRequest(String tokenToRevoke, TokenTypeHint tokenTypeHint) { final OAuthRequest request = new OAuthRequest(Verb.POST, api.getRevokeTokenEndpoint()); @@ -438,45 +496,11 @@ public Void convert(Response response) throws IOException { private void checkForErrorRevokeToken(Response response) throws IOException { if (response.getCode() != 200) { - OAuth2AccessTokenJsonExtractor.instance().generateError(response.getBody()); + OAuth2AccessTokenJsonExtractor.instance().generateError(response); } } - public OAuth2Authorization extractAuthorization(String redirectLocation) { - final OAuth2Authorization authorization = new OAuth2Authorization(); - int end = redirectLocation.indexOf('#'); - if (end == -1) { - end = redirectLocation.length(); - } - for (String param : redirectLocation.substring(redirectLocation.indexOf('?') + 1, end).split("&")) { - final String[] keyValue = param.split("="); - if (keyValue.length == 2) { - try { - switch (keyValue[0]) { - case "code": - authorization.setCode(URLDecoder.decode(keyValue[1], "UTF-8")); - break; - case "state": - authorization.setState(URLDecoder.decode(keyValue[1], "UTF-8")); - break; - default: //just ignore any other param; - } - } catch (UnsupportedEncodingException ueE) { - throw new IllegalStateException("jvm without UTF-8, really?", ueE); - } - } - } - return authorization; - } - - public String getResponseType() { - return responseType; - } - - public String getDefaultScope() { - return defaultScope; - } - + // ===== device Authorisation codes methods ===== protected OAuthRequest createDeviceAuthorizationCodesRequest(String scope) { final OAuthRequest request = new OAuthRequest(Verb.POST, api.getDeviceAuthorizationEndpoint()); request.addParameter(OAuthConstants.CLIENT_ID, getApiKey()); @@ -558,6 +582,7 @@ public Future getDeviceAuthorizationCodesAsync(String scope return getDeviceAuthorizationCodes(scope, null); } + // ===== get AccessToken Device Authorisation grant flow methods ===== protected OAuthRequest createAccessTokenDeviceAuthorizationGrantRequest(DeviceAuthorization deviceAuthorization) { final OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint()); request.addParameter(OAuthConstants.GRANT_TYPE, "urn:ietf:params:oauth:grant-type:device_code"); @@ -648,12 +673,4 @@ public OAuth2AccessToken pollAccessTokenDeviceAuthorizationGrant(DeviceAuthoriza Thread.sleep(intervalMillis); } } - - private void logRequestWithParams(String requestDescription, OAuthRequest request) { - if (isDebug()) { - log("created " + requestDescription + " request with body params [%s], query string params [%s]", - request.getBodyParams().asFormUrlEncodedString(), - request.getQueryStringParams().asFormUrlEncodedString()); - } - } } diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/oauth2/clientauthentication/HttpBasicAuthenticationScheme.java b/scribejava-core/src/main/java/com/github/scribejava/core/oauth2/clientauthentication/HttpBasicAuthenticationScheme.java index 509869d51..3931f6f7b 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/oauth2/clientauthentication/HttpBasicAuthenticationScheme.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/oauth2/clientauthentication/HttpBasicAuthenticationScheme.java @@ -1,6 +1,6 @@ package com.github.scribejava.core.oauth2.clientauthentication; -import com.github.scribejava.core.java8.Base64; +import com.github.scribejava.core.base64.Base64; import com.github.scribejava.core.model.OAuthConstants; import com.github.scribejava.core.model.OAuthRequest; import java.nio.charset.Charset; @@ -14,8 +14,6 @@ */ public class HttpBasicAuthenticationScheme implements ClientAuthentication { - private final Base64.Encoder base64Encoder = Base64.getEncoder(); - protected HttpBasicAuthenticationScheme() { } @@ -32,8 +30,7 @@ public static HttpBasicAuthenticationScheme instance() { public void addClientAuthentication(OAuthRequest request, String apiKey, String apiSecret) { if (apiKey != null && apiSecret != null) { request.addHeader(OAuthConstants.HEADER, OAuthConstants.BASIC + ' ' - + base64Encoder.encodeToString( - String.format("%s:%s", apiKey, apiSecret).getBytes(Charset.forName("UTF-8")))); + + Base64.encode(String.format("%s:%s", apiKey, apiSecret).getBytes(Charset.forName("UTF-8")))); } } diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/pkce/PKCECodeChallengeMethod.java b/scribejava-core/src/main/java/com/github/scribejava/core/pkce/PKCECodeChallengeMethod.java index 08bb60c5b..374f80336 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/pkce/PKCECodeChallengeMethod.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/pkce/PKCECodeChallengeMethod.java @@ -1,17 +1,15 @@ package com.github.scribejava.core.pkce; -import com.github.scribejava.core.java8.Base64; +import com.github.scribejava.core.base64.Base64; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public enum PKCECodeChallengeMethod { S256 { - private final Base64.Encoder base64Encoder = Base64.getUrlEncoder().withoutPadding(); - @Override public String transform2CodeChallenge(String codeVerifier) throws NoSuchAlgorithmException { - return base64Encoder.encodeToString( + return Base64.encodeUrlWithoutPadding( MessageDigest.getInstance("SHA-256").digest(codeVerifier.getBytes(StandardCharsets.US_ASCII))); } }, diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/pkce/PKCEService.java b/scribejava-core/src/main/java/com/github/scribejava/core/pkce/PKCEService.java index eea3e7b94..23a5347ec 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/pkce/PKCEService.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/pkce/PKCEService.java @@ -1,6 +1,6 @@ package com.github.scribejava.core.pkce; -import com.github.scribejava.core.java8.Base64; +import com.github.scribejava.core.base64.Base64; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; @@ -11,7 +11,6 @@ public class PKCEService { private static final SecureRandom RANDOM = new SecureRandom(); - private static final Base64.Encoder BASE_64_ENCODER = Base64.getUrlEncoder().withoutPadding(); /** * number of octets to randomly generate. */ @@ -44,7 +43,7 @@ public PKCE generatePKCE() { } public PKCE generatePKCE(byte[] randomBytes) { - final String codeVerifier = BASE_64_ENCODER.encodeToString(randomBytes); + final String codeVerifier = Base64.encodeUrlWithoutPadding(randomBytes); final PKCE pkce = new PKCE(); pkce.setCodeVerifier(codeVerifier); diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/revoke/OAuth2RevokeTokenResponseConverter.java b/scribejava-core/src/main/java/com/github/scribejava/core/revoke/OAuth2RevokeTokenResponseConverter.java index b287d7868..dc595a410 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/revoke/OAuth2RevokeTokenResponseConverter.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/revoke/OAuth2RevokeTokenResponseConverter.java @@ -8,7 +8,7 @@ public class OAuth2RevokeTokenResponseConverter { public Void convert(Response response) throws IOException { if (response.getCode() != 200) { - OAuth2AccessTokenJsonExtractor.instance().generateError(response.getBody()); + OAuth2AccessTokenJsonExtractor.instance().generateError(response); } return null; } diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/services/HMACSha1SignatureService.java b/scribejava-core/src/main/java/com/github/scribejava/core/services/HMACSha1SignatureService.java index 82d1135be..363011a13 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/services/HMACSha1SignatureService.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/services/HMACSha1SignatureService.java @@ -1,5 +1,6 @@ package com.github.scribejava.core.services; +import com.github.scribejava.core.base64.Base64; import java.io.UnsupportedEncodingException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -10,8 +11,7 @@ import com.github.scribejava.core.utils.Preconditions; /** - * HMAC-SHA1 implementation of {@link SignatureService} - * https://tools.ietf.org/html/rfc5849#section-3.4.2 + * HMAC-SHA1 implementation of {@link SignatureService} https://tools.ietf.org/html/rfc5849#section-3.4.2 */ public class HMACSha1SignatureService implements SignatureService { @@ -27,8 +27,8 @@ public class HMACSha1SignatureService implements SignatureService { @Override public String getSignature(String baseString, String apiSecret, String tokenSecret) { try { - Preconditions.checkEmptyString(baseString, "Base string cant be null or empty string"); - Preconditions.checkEmptyString(apiSecret, "Api secret cant be null or empty string"); + Preconditions.checkEmptyString(baseString, "Base string can't be null or empty string"); + Preconditions.checkNotNull(apiSecret, "Api secret can't be null"); return doSign(baseString, OAuthEncoder.encode(apiSecret) + '&' + OAuthEncoder.encode(tokenSecret)); } catch (UnsupportedEncodingException | NoSuchAlgorithmException | InvalidKeyException | RuntimeException e) { throw new OAuthSignatureException(baseString, e); @@ -41,7 +41,7 @@ private String doSign(String toSign, String keyString) throws UnsupportedEncodin final Mac mac = Mac.getInstance(HMAC_SHA1); mac.init(key); final byte[] bytes = mac.doFinal(toSign.getBytes(UTF8)); - return BASE_64_ENCODER.encodeToString(bytes).replace(CARRIAGE_RETURN, EMPTY_STRING); + return Base64.encode(bytes).replace(CARRIAGE_RETURN, EMPTY_STRING); } /** diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/services/PlaintextSignatureService.java b/scribejava-core/src/main/java/com/github/scribejava/core/services/PlaintextSignatureService.java index 33661f10a..143348484 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/services/PlaintextSignatureService.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/services/PlaintextSignatureService.java @@ -17,7 +17,7 @@ public class PlaintextSignatureService implements SignatureService { @Override public String getSignature(String baseString, String apiSecret, String tokenSecret) { try { - Preconditions.checkEmptyString(apiSecret, "Api secret cant be null or empty string"); + Preconditions.checkNotNull(apiSecret, "Api secret can't be null"); return OAuthEncoder.encode(apiSecret) + '&' + OAuthEncoder.encode(tokenSecret); } catch (Exception e) { throw new OAuthSignatureException(baseString, e); diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/services/RSASha1SignatureService.java b/scribejava-core/src/main/java/com/github/scribejava/core/services/RSASha1SignatureService.java index 9cb460e7b..dc16eace4 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/services/RSASha1SignatureService.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/services/RSASha1SignatureService.java @@ -1,5 +1,6 @@ package com.github.scribejava.core.services; +import com.github.scribejava.core.base64.Base64; import java.security.PrivateKey; import java.security.Signature; import java.security.SignatureException; @@ -32,9 +33,9 @@ public String getSignature(String baseString, String apiSecret, String tokenSecr final Signature signature = Signature.getInstance(RSA_SHA1); signature.initSign(privateKey); signature.update(baseString.getBytes(UTF8)); - return BASE_64_ENCODER.encodeToString(signature.sign()); - } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException | UnsupportedEncodingException | - RuntimeException e) { + return Base64.encode(signature.sign()); + } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException | UnsupportedEncodingException + | RuntimeException e) { throw new OAuthSignatureException(baseString, e); } } diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/services/SignatureService.java b/scribejava-core/src/main/java/com/github/scribejava/core/services/SignatureService.java index 8cd4f2372..10c50f3ae 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/services/SignatureService.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/services/SignatureService.java @@ -1,13 +1,9 @@ package com.github.scribejava.core.services; -import com.github.scribejava.core.java8.Base64; - /** - * Signs a base string, returning the OAuth signature - * https://tools.ietf.org/html/rfc5849#section-3.4 + * Signs a base string, returning the OAuth signature https://tools.ietf.org/html/rfc5849#section-3.4 */ public interface SignatureService { - Base64.Encoder BASE_64_ENCODER = Base64.getEncoder(); /** * Returns the signature diff --git a/scribejava-core/src/test/java/com/github/scribejava/core/base64/Base64Test.java b/scribejava-core/src/test/java/com/github/scribejava/core/base64/Base64Test.java new file mode 100644 index 000000000..4d345b77d --- /dev/null +++ b/scribejava-core/src/test/java/com/github/scribejava/core/base64/Base64Test.java @@ -0,0 +1,213 @@ +package com.github.scribejava.core.base64; + +import java.io.UnsupportedEncodingException; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; + +public class Base64Test { + + private Base64 java8Base64; + private Base64 commonsCodecBase64; + private Base64 jaxbBase64; + private Base64 jaxb230Base64; + private byte[] helloWorldBytes; + private byte[] helloWorldTwoLinesBytes; + private byte[] helloWorldTwoLinesAndNewLineBytes; + private byte[] helloWorldDifferentCharsBytes; + private byte[] bytes; + private byte[] allBytes; + + @Before + public void setUp() throws UnsupportedEncodingException { + helloWorldBytes = "Hello World".getBytes("UTF-8"); + helloWorldTwoLinesBytes = "Hello World\r\nNew Line2".getBytes("UTF-8"); + helloWorldTwoLinesAndNewLineBytes = "Hello World\r\nSecond Line\r\n".getBytes("UTF-8"); + helloWorldDifferentCharsBytes = ("`1234567890-=~!@#$%^&*()_+ёЁ\"№;:?qwertyuiop[]asdfghjkl;'zxcvbnm,./QWERTYUIOP" + + "{}|ASDFGHJKL:ZXCVBNM<>?йфяцычувскамепинртгоьшлбщдюзж.хэъ\\ЙФЯЦЫЧУВСКАМЕПИНРТГОЬШЛБЩДЮЗЖ,ХЭЪ/\r\t\f\'" + + "\b\n").getBytes("UTF-8"); + bytes = new byte[]{48, -126, 2, 118, 2, 1, 0, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0, 4, -126, + 2, 96, 48, -126, 2, 92, 2, 1, 0, 2, -127, -127, 0, -61, -48, -28, 16, -116, -58, 85, 42, -39, 54, 50, -119, + 18, 40, 17, 75, 51, -24, 113, -109, 38, 17, -18, 106, -60, -74, -97, 29, 82, 123, -128, -88, -34, 92, 112, + -57, 43, -101, 85, -47, 99, -16, 11, -95, 28, -46, 82, -104, -101, -29, -106, -106, -45, -80, 99, -93, 45, + -102, 107, 31, 32, -60, 13, -46, 102, 127, 81, 94, -98, -56, 117, 50, 21, 39, 5, -98, 26, -18, -30, -21, + 102, -78, -77, 20, 113, -55, 117, -87, -105, -10, -100, 90, -92, 31, 61, -68, -73, -121, -108, 42, 45, -10, + 21, 87, 118, -74, 71, -100, -37, 96, -24, 87, 102, 68, -95, -1, -14, 6, -20, -14, 32, -14, 33, -84, -123, + -65, 54, 3, 2, 3, 1, 0, 1, 2, -127, -128, 62, 115, -45, 41, 76, 28, -67, 113, 11, 17, -12, 16, 47, -112, 67, + -29, -66, 76, 118, 92, -66, 25, -99, -10, -61, -126, -109, 64, -32, -37, -82, -17, 44, -20, 66, -77, -29, + 62, -119, -94, 92, -61, 100, -110, 32, 5, 28, 126, -69, -55, 92, 112, 2, 88, 17, -113, 43, -82, 66, 88, 13, + 53, 58, 74, -65, 36, 45, 93, -63, -15, 125, -7, -44, -45, -51, -76, 86, 97, 54, -36, -49, -117, -18, 56, 54, + 78, 80, 119, -6, -75, 39, 16, 57, -125, -68, 42, 50, -114, 92, 6, 13, 30, -91, 53, -66, -19, -20, 88, 32, + -38, 36, 126, -119, -86, 47, -46, 37, 115, -49, -23, 125, -61, 75, 37, 70, 92, -122, -79, 2, 65, 0, -11, + -105, 91, 105, -73, 54, 97, 96, -87, -16, -15, -73, 15, 31, -80, -96, -74, -53, -54, 53, -17, -9, 39, 62, + 58, 51, 68, 107, 86, 111, -62, -48, -125, 117, 66, 111, -55, 27, 56, 81, -50, 96, -47, -102, -50, -83, -52, + -17, -20, 3, -42, -94, 11, 23, 104, 127, 29, -25, 32, 43, -41, -112, -83, -99, 2, 65, 0, -52, 29, 122, 9, + 49, -14, -118, 110, -79, 107, 76, -88, 4, -49, 40, 32, 59, 88, 45, -71, 62, 78, 93, -121, -123, 123, 3, 4, + 111, -112, 27, 12, -115, -123, 125, 39, 54, 96, -2, -46, 30, 40, -4, -119, 13, -121, 118, -23, 1, -83, -76, + -26, -117, -86, -79, -121, 113, -26, 33, 30, 124, 35, -16, 31, 2, 65, 0, -47, -113, 111, -81, 75, 104, -103, + -69, 20, 7, -57, 25, -65, 75, -7, 57, -118, 1, 102, -16, -109, 108, -64, 13, -73, 55, -37, -32, 3, -121, + -90, 34, -86, -87, -70, 33, 12, -25, -81, 45, 14, -1, 74, -101, -32, 84, 41, -107, 104, 60, -10, 62, -101, + 92, 68, 12, -124, 5, -98, 76, 10, -53, 39, 121, 2, 64, 7, 106, 102, -67, -96, -57, -20, 9, -101, 126, -121, + 121, 111, 59, 75, 124, -24, 75, 10, -42, 57, 18, 69, -55, -97, -86, -39, 112, 54, -47, 104, 122, 43, 70, 23, + 70, -18, 109, -43, -76, 50, -114, 80, -90, 118, 12, 94, -32, -106, 68, 6, 87, 125, -23, -124, -85, -92, 18, + -75, 79, 83, 57, 71, 7, 2, 64, 73, -64, -3, 78, -90, -122, -64, -99, -29, -71, 75, 21, -74, -24, -43, -37, + 116, -89, 31, -115, -30, 50, 8, 23, 79, -71, -68, -39, 36, -23, 60, 102, -90, -42, 19, -33, -102, -85, -74, + 103, 73, -30, 120, -15, 104, -9, 110, -24, -127, 14, -57, -44, 67, 9, 80, 120, 42, 94, 107, -81, -109, 101, + -1, 91}; + + allBytes = new byte[]{-128, -127, -126, -125, -124, -123, -122, -121, -120, -119, -118, -117, -116, -115, -114, + -113, -112, -111, -110, -109, -108, -107, -106, -105, -104, -103, -102, -101, -100, -99, -98, -97, -96, -95, + -94, -93, -92, -91, -90, -89, -88, -87, -86, -85, -84, -83, -82, -81, -80, -79, -78, -77, -76, -75, -74, + -73, -72, -71, -70, -69, -68, -67, -66, -65, -64, -63, -62, -61, -60, -59, -58, -57, -56, -55, -54, -53, + -52, -51, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, + -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, + -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126, 127}; + + java8Base64 = new Java8Base64(); + commonsCodecBase64 = new CommonsCodecBase64(); + jaxbBase64 = new JaxbBase64(); + jaxb230Base64 = new Jaxb230Base64(); + } + + @Test + public void allImplementationsAreAvailable() { + assertTrue(Java8Base64.isAvailable()); + assertTrue(CommonsCodecBase64.isAvailable()); + assertTrue(JaxbBase64.isAvailable()); + assertTrue(Jaxb230Base64.isAvailable()); + } + + @Test + public void testEncode() { + final String helloWorldEncoded = "SGVsbG8gV29ybGQ="; + final String helloWorldTwoLinesEncoded = "SGVsbG8gV29ybGQNCk5ldyBMaW5lMg=="; + final String helloWorldTwoLinesAndNewLineEncoded = "SGVsbG8gV29ybGQNClNlY29uZCBMaW5lDQo="; + final String helloWorldDifferentCharsEncoded = "YDEyMzQ1Njc4OTAtPX4hQCMkJV4mKigpXyvRkdCBIuKEljs6P3F3ZXJ0eXVpb3B" + + "bXWFzZGZnaGprbDsnenhjdmJubSwuL1FXRVJUWVVJT1B7fXxBU0RGR0hKS0w6WlhDVkJOTTw+P9C50YTRj9GG0YvRh9GD0LLRgdC" + + "60LDQvNC10L/QuNC90YDRgtCz0L7RjNGI0LvQsdGJ0LTRjtC30LYu0YXRjdGKXNCZ0KTQr9Cm0KvQp9Cj0JLQodCa0JDQnNCV0J/" + + "QmNCd0KDQotCT0J7QrNCo0JvQkdCp0JTQrtCX0JYs0KXQrdCqLw0JDCcICg=="; + final String str = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMPQ5BCMxlUq2TYy" + + "iRIoEUsz6HGTJhHuasS2nx1Se4Co3lxwxyubVdFj8AuhHNJSmJvjlpbTsGOjLZpr" + + "HyDEDdJmf1Fensh1MhUnBZ4a7uLrZrKzFHHJdamX9pxapB89vLeHlCot9hVXdrZH" + + "nNtg6FdmRKH/8gbs8iDyIayFvzYDAgMBAAECgYA+c9MpTBy9cQsR9BAvkEPjvkx2" + + "XL4ZnfbDgpNA4Nuu7yzsQrPjPomiXMNkkiAFHH67yVxwAlgRjyuuQlgNNTpKvyQt" + + "XcHxffnU0820VmE23M+L7jg2TlB3+rUnEDmDvCoyjlwGDR6lNb7t7Fgg2iR+iaov" + + "0iVzz+l9w0slRlyGsQJBAPWXW2m3NmFgqfDxtw8fsKC2y8o17/cnPjozRGtWb8LQ" + + "g3VCb8kbOFHOYNGazq3M7+wD1qILF2h/HecgK9eQrZ0CQQDMHXoJMfKKbrFrTKgE" + + "zyggO1gtuT5OXYeFewMEb5AbDI2FfSc2YP7SHij8iQ2HdukBrbTmi6qxh3HmIR58" + + "I/AfAkEA0Y9vr0tombsUB8cZv0v5OYoBZvCTbMANtzfb4AOHpiKqqbohDOevLQ7/" + + "SpvgVCmVaDz2PptcRAyEBZ5MCssneQJAB2pmvaDH7Ambfod5bztLfOhLCtY5EkXJ" + + "n6rZcDbRaHorRhdG7m3VtDKOUKZ2DF7glkQGV33phKukErVPUzlHBwJAScD9TqaG" + + "wJ3juUsVtujV23SnH43iMggXT7m82STpPGam1hPfmqu2Z0niePFo927ogQ7H1EMJ" + + "UHgqXmuvk2X/Ww=="; + + final String allBytesStr = "gIGCg4SFhoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnp+goaKjpKWmp6ipqqusra6vsLGys7S1tre4ubq7vL2" + + "+v8DBwsPExcbHyMnKy8zNzs/Q0dLT1NXW19jZ2tvc3d7f4OHi4+Tl5ufo6err7O3u7/Dx8vP09fb3+Pn6+/z9/v8AAQIDBAUGBwg" + + "JCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4/QEFCQ0RFRkdISUpLTE1OT1BRUlN" + + "UVVZXWFlaW1xdXl9gYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1+fw=="; + + assertEquals(helloWorldEncoded, java8Base64.internalEncode(helloWorldBytes)); + assertEquals(helloWorldTwoLinesEncoded, java8Base64.internalEncode(helloWorldTwoLinesBytes)); + assertEquals(helloWorldTwoLinesAndNewLineEncoded, + java8Base64.internalEncode(helloWorldTwoLinesAndNewLineBytes)); + assertEquals(helloWorldDifferentCharsEncoded, java8Base64.internalEncode(helloWorldDifferentCharsBytes)); + assertEquals(str, java8Base64.internalEncode(bytes)); + assertEquals(allBytesStr, java8Base64.internalEncode(allBytes)); + + assertEquals(helloWorldEncoded, commonsCodecBase64.internalEncode(helloWorldBytes)); + assertEquals(helloWorldTwoLinesEncoded, commonsCodecBase64.internalEncode(helloWorldTwoLinesBytes)); + assertEquals(helloWorldTwoLinesAndNewLineEncoded, + commonsCodecBase64.internalEncode(helloWorldTwoLinesAndNewLineBytes)); + assertEquals(helloWorldDifferentCharsEncoded, commonsCodecBase64.internalEncode(helloWorldDifferentCharsBytes)); + assertEquals(str, commonsCodecBase64.internalEncode(bytes)); + assertEquals(allBytesStr, commonsCodecBase64.internalEncode(allBytes)); + + assertEquals(helloWorldEncoded, jaxbBase64.internalEncode(helloWorldBytes)); + assertEquals(helloWorldTwoLinesEncoded, jaxbBase64.internalEncode(helloWorldTwoLinesBytes)); + assertEquals(helloWorldTwoLinesAndNewLineEncoded, jaxbBase64.internalEncode(helloWorldTwoLinesAndNewLineBytes)); + assertEquals(helloWorldDifferentCharsEncoded, jaxbBase64.internalEncode(helloWorldDifferentCharsBytes)); + assertEquals(str, jaxbBase64.internalEncode(bytes)); + assertEquals(allBytesStr, jaxbBase64.internalEncode(allBytes)); + + assertEquals(helloWorldEncoded, jaxb230Base64.internalEncode(helloWorldBytes)); + assertEquals(helloWorldTwoLinesEncoded, jaxb230Base64.internalEncode(helloWorldTwoLinesBytes)); + assertEquals(helloWorldTwoLinesAndNewLineEncoded, + jaxb230Base64.internalEncode(helloWorldTwoLinesAndNewLineBytes)); + assertEquals(helloWorldDifferentCharsEncoded, jaxb230Base64.internalEncode(helloWorldDifferentCharsBytes)); + assertEquals(str, jaxb230Base64.internalEncode(bytes)); + assertEquals(allBytesStr, jaxb230Base64.internalEncode(allBytes)); + } + + @Test + public void testEncodeUrlWithoutPadding() { + final String helloWorldEncoded = "SGVsbG8gV29ybGQ"; + final String helloWorldTwoLinesEncoded = "SGVsbG8gV29ybGQNCk5ldyBMaW5lMg"; + final String helloWorldTwoLinesAndNewLineEncoded = "SGVsbG8gV29ybGQNClNlY29uZCBMaW5lDQo"; + final String helloWorldDifferentCharsEncoded = "YDEyMzQ1Njc4OTAtPX4hQCMkJV4mKigpXyvRkdCBIuKEljs6P3F3ZXJ0eXVpb3B" + + "bXWFzZGZnaGprbDsnenhjdmJubSwuL1FXRVJUWVVJT1B7fXxBU0RGR0hKS0w6WlhDVkJOTTw-P9C50YTRj9GG0YvRh9GD0LLRgdC" + + "60LDQvNC10L_QuNC90YDRgtCz0L7RjNGI0LvQsdGJ0LTRjtC30LYu0YXRjdGKXNCZ0KTQr9Cm0KvQp9Cj0JLQodCa0JDQnNCV0J_" + + "QmNCd0KDQotCT0J7QrNCo0JvQkdCp0JTQrtCX0JYs0KXQrdCqLw0JDCcICg"; + final String str = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMPQ5BCMxlUq2TYy" + + "iRIoEUsz6HGTJhHuasS2nx1Se4Co3lxwxyubVdFj8AuhHNJSmJvjlpbTsGOjLZpr" + + "HyDEDdJmf1Fensh1MhUnBZ4a7uLrZrKzFHHJdamX9pxapB89vLeHlCot9hVXdrZH" + + "nNtg6FdmRKH_8gbs8iDyIayFvzYDAgMBAAECgYA-c9MpTBy9cQsR9BAvkEPjvkx2" + + "XL4ZnfbDgpNA4Nuu7yzsQrPjPomiXMNkkiAFHH67yVxwAlgRjyuuQlgNNTpKvyQt" + + "XcHxffnU0820VmE23M-L7jg2TlB3-rUnEDmDvCoyjlwGDR6lNb7t7Fgg2iR-iaov" + + "0iVzz-l9w0slRlyGsQJBAPWXW2m3NmFgqfDxtw8fsKC2y8o17_cnPjozRGtWb8LQ" + + "g3VCb8kbOFHOYNGazq3M7-wD1qILF2h_HecgK9eQrZ0CQQDMHXoJMfKKbrFrTKgE" + + "zyggO1gtuT5OXYeFewMEb5AbDI2FfSc2YP7SHij8iQ2HdukBrbTmi6qxh3HmIR58" + + "I_AfAkEA0Y9vr0tombsUB8cZv0v5OYoBZvCTbMANtzfb4AOHpiKqqbohDOevLQ7_" + + "SpvgVCmVaDz2PptcRAyEBZ5MCssneQJAB2pmvaDH7Ambfod5bztLfOhLCtY5EkXJ" + + "n6rZcDbRaHorRhdG7m3VtDKOUKZ2DF7glkQGV33phKukErVPUzlHBwJAScD9TqaG" + + "wJ3juUsVtujV23SnH43iMggXT7m82STpPGam1hPfmqu2Z0niePFo927ogQ7H1EMJ" + + "UHgqXmuvk2X_Ww"; + + final String allBytesStr = "gIGCg4SFhoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnp-goaKjpKWmp6ipqqusra6vsLGys7S1tre4ubq7vL2" + + "-v8DBwsPExcbHyMnKy8zNzs_Q0dLT1NXW19jZ2tvc3d7f4OHi4-Tl5ufo6err7O3u7_Dx8vP09fb3-Pn6-_z9_v8AAQIDBAUGBwg" + + "JCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4_QEFCQ0RFRkdISUpLTE1OT1BRUlN" + + "UVVZXWFlaW1xdXl9gYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1-fw"; + + assertEquals(helloWorldEncoded, java8Base64.internalEncodeUrlWithoutPadding(helloWorldBytes)); + assertEquals(helloWorldTwoLinesEncoded, java8Base64.internalEncodeUrlWithoutPadding(helloWorldTwoLinesBytes)); + assertEquals(helloWorldTwoLinesAndNewLineEncoded, + java8Base64.internalEncodeUrlWithoutPadding(helloWorldTwoLinesAndNewLineBytes)); + assertEquals(helloWorldDifferentCharsEncoded, + java8Base64.internalEncodeUrlWithoutPadding(helloWorldDifferentCharsBytes)); + assertEquals(str, java8Base64.internalEncodeUrlWithoutPadding(bytes)); + assertEquals(allBytesStr, java8Base64.internalEncodeUrlWithoutPadding(allBytes)); + + assertEquals(helloWorldEncoded, commonsCodecBase64.internalEncodeUrlWithoutPadding(helloWorldBytes)); + assertEquals(helloWorldTwoLinesEncoded, + commonsCodecBase64.internalEncodeUrlWithoutPadding(helloWorldTwoLinesBytes)); + assertEquals(helloWorldTwoLinesAndNewLineEncoded, + commonsCodecBase64.internalEncodeUrlWithoutPadding(helloWorldTwoLinesAndNewLineBytes)); + assertEquals(helloWorldDifferentCharsEncoded, + commonsCodecBase64.internalEncodeUrlWithoutPadding(helloWorldDifferentCharsBytes)); + assertEquals(str, commonsCodecBase64.internalEncodeUrlWithoutPadding(bytes)); + assertEquals(allBytesStr, commonsCodecBase64.internalEncodeUrlWithoutPadding(allBytes)); + + assertEquals(helloWorldEncoded, jaxbBase64.internalEncodeUrlWithoutPadding(helloWorldBytes)); + assertEquals(helloWorldTwoLinesEncoded, jaxbBase64.internalEncodeUrlWithoutPadding(helloWorldTwoLinesBytes)); + assertEquals(helloWorldTwoLinesAndNewLineEncoded, + jaxbBase64.internalEncodeUrlWithoutPadding(helloWorldTwoLinesAndNewLineBytes)); + assertEquals(helloWorldDifferentCharsEncoded, + jaxbBase64.internalEncodeUrlWithoutPadding(helloWorldDifferentCharsBytes)); + assertEquals(str, jaxbBase64.internalEncodeUrlWithoutPadding(bytes)); + assertEquals(allBytesStr, jaxbBase64.internalEncodeUrlWithoutPadding(allBytes)); + + assertEquals(helloWorldEncoded, jaxb230Base64.internalEncodeUrlWithoutPadding(helloWorldBytes)); + assertEquals(helloWorldTwoLinesEncoded, jaxb230Base64.internalEncodeUrlWithoutPadding(helloWorldTwoLinesBytes)); + assertEquals(helloWorldTwoLinesAndNewLineEncoded, + jaxb230Base64.internalEncodeUrlWithoutPadding(helloWorldTwoLinesAndNewLineBytes)); + assertEquals(helloWorldDifferentCharsEncoded, + jaxb230Base64.internalEncodeUrlWithoutPadding(helloWorldDifferentCharsBytes)); + assertEquals(str, jaxb230Base64.internalEncodeUrlWithoutPadding(bytes)); + assertEquals(allBytesStr, jaxb230Base64.internalEncodeUrlWithoutPadding(allBytes)); + } +} diff --git a/scribejava-core/src/test/java/com/github/scribejava/core/extractors/BaseStringExtractorTest.java b/scribejava-core/src/test/java/com/github/scribejava/core/extractors/BaseStringExtractorTest.java index bc833db08..89865a637 100644 --- a/scribejava-core/src/test/java/com/github/scribejava/core/extractors/BaseStringExtractorTest.java +++ b/scribejava-core/src/test/java/com/github/scribejava/core/extractors/BaseStringExtractorTest.java @@ -7,6 +7,8 @@ import com.github.scribejava.core.exceptions.OAuthParametersMissingException; import com.github.scribejava.core.model.OAuthRequest; import com.github.scribejava.core.model.Verb; +import static org.junit.Assert.assertThrows; +import org.junit.function.ThrowingRunnable; public class BaseStringExtractorTest { @@ -83,15 +85,23 @@ public void shouldExcludePort443v2() { assertEquals(expected, baseString); } - @Test(expected = IllegalArgumentException.class) public void shouldThrowExceptionIfRquestIsNull() { - extractor.extract(null); + assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(null); + } + }); } - @Test(expected = OAuthParametersMissingException.class) public void shouldThrowExceptionIfRquestHasNoOAuthParameters() { final OAuthRequest request = new OAuthRequest(Verb.GET, "http://example.com"); - extractor.extract(request); + assertThrows(OAuthParametersMissingException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(request); + } + }); } @Test diff --git a/scribejava-core/src/test/java/com/github/scribejava/core/extractors/HeaderExtractorTest.java b/scribejava-core/src/test/java/com/github/scribejava/core/extractors/HeaderExtractorTest.java index c1658fc7e..b515908c8 100644 --- a/scribejava-core/src/test/java/com/github/scribejava/core/extractors/HeaderExtractorTest.java +++ b/scribejava-core/src/test/java/com/github/scribejava/core/extractors/HeaderExtractorTest.java @@ -2,12 +2,14 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertThrows; import org.junit.Before; import org.junit.Test; import com.github.scribejava.core.exceptions.OAuthParametersMissingException; import com.github.scribejava.core.model.OAuthRequest; import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.ObjectMother; +import org.junit.function.ThrowingRunnable; public class HeaderExtractorTest { @@ -36,21 +38,29 @@ public void shouldExtractStandardHeader() { assertTrue(header.contains(timestamp)); // Assert that header only contains the checked elements above and nothing else assertEquals(", , , ", - header.replaceFirst(oauth, "") - .replaceFirst(callback, "") - .replaceFirst(signature, "") - .replaceFirst(key, "") - .replaceFirst(timestamp, "")); + header.replaceFirst(oauth, "") + .replaceFirst(callback, "") + .replaceFirst(signature, "") + .replaceFirst(key, "") + .replaceFirst(timestamp, "")); } - @Test(expected = IllegalArgumentException.class) public void shouldExceptionIfRequestIsNull() { - extractor.extract(null); + assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(null); + } + }); } - @Test(expected = OAuthParametersMissingException.class) public void shouldExceptionIfRequestHasNoOAuthParams() { final OAuthRequest emptyRequest = new OAuthRequest(Verb.GET, "http://example.com"); - extractor.extract(emptyRequest); + assertThrows(OAuthParametersMissingException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(emptyRequest); + } + }); } } diff --git a/scribejava-core/src/test/java/com/github/scribejava/core/extractors/OAuth1AccessTokenExtractorTest.java b/scribejava-core/src/test/java/com/github/scribejava/core/extractors/OAuth1AccessTokenExtractorTest.java index 58ed0e93d..b62d9bf1e 100644 --- a/scribejava-core/src/test/java/com/github/scribejava/core/extractors/OAuth1AccessTokenExtractorTest.java +++ b/scribejava-core/src/test/java/com/github/scribejava/core/extractors/OAuth1AccessTokenExtractorTest.java @@ -10,6 +10,8 @@ import java.util.Collections; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import org.junit.function.ThrowingRunnable; public class OAuth1AccessTokenExtractorTest { @@ -65,34 +67,50 @@ public void shouldExtractTokenWithEmptySecret() throws IOException { assertEquals("", extracted.getTokenSecret()); } - @Test(expected = OAuthException.class) public void shouldThrowExceptionIfTokenIsAbsent() throws IOException { final String responseBody = "oauth_secret=hh5s93j4hdidpola&callback_confirmed=true"; try (Response response = ok(responseBody)) { - extractor.extract(response); + assertThrows(OAuthException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(response); + } + }); } } - @Test(expected = OAuthException.class) public void shouldThrowExceptionIfSecretIsAbsent() throws IOException { final String responseBody = "oauth_token=hh5s93j4hdidpola&callback_confirmed=true"; try (Response response = ok(responseBody)) { - extractor.extract(response); + assertThrows(OAuthException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(response); + } + }); } } - @Test(expected = IllegalArgumentException.class) public void shouldThrowExceptionIfResponseIsNull() throws IOException { try (Response response = ok(null)) { - extractor.extract(response); + assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(response); + } + }); } } - @Test(expected = IllegalArgumentException.class) public void shouldThrowExceptionIfResponseIsEmptyString() throws IOException { final String responseBody = ""; try (Response response = ok(responseBody)) { - extractor.extract(response); + assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(response); + } + }); } } diff --git a/scribejava-core/src/test/java/com/github/scribejava/core/extractors/OAuth2AccessTokenExtractorTest.java b/scribejava-core/src/test/java/com/github/scribejava/core/extractors/OAuth2AccessTokenExtractorTest.java index 28d9a569b..4f71c011d 100644 --- a/scribejava-core/src/test/java/com/github/scribejava/core/extractors/OAuth2AccessTokenExtractorTest.java +++ b/scribejava-core/src/test/java/com/github/scribejava/core/extractors/OAuth2AccessTokenExtractorTest.java @@ -10,6 +10,8 @@ import java.util.Collections; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import org.junit.function.ThrowingRunnable; public class OAuth2AccessTokenExtractorTest { @@ -70,34 +72,50 @@ public void shouldExtractTokenFromResponseWithManyParameters() throws IOExceptio assertEquals("foo1234", extracted.getAccessToken()); } - @Test(expected = OAuthException.class) public void shouldThrowExceptionIfErrorResponse() throws IOException { final String responseBody = ""; try (Response response = error(responseBody)) { - extractor.extract(response); + assertThrows(OAuthException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(response); + } + }); } } - @Test(expected = OAuthException.class) public void shouldThrowExceptionIfTokenIsAbsent() throws IOException { final String responseBody = "&expires=5108"; try (Response response = ok(responseBody)) { - extractor.extract(response); + assertThrows(OAuthException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(response); + } + }); } } - @Test(expected = IllegalArgumentException.class) public void shouldThrowExceptionIfResponseIsNull() throws IOException { try (Response response = ok(null)) { - extractor.extract(response); + assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(response); + } + }); } } - @Test(expected = IllegalArgumentException.class) public void shouldThrowExceptionIfResponseIsEmptyString() throws IOException { final String responseBody = ""; try (Response response = ok(responseBody)) { - extractor.extract(response); + assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(response); + } + }); } } diff --git a/scribejava-core/src/test/java/com/github/scribejava/core/extractors/OAuth2AccessTokenJsonExtractorTest.java b/scribejava-core/src/test/java/com/github/scribejava/core/extractors/OAuth2AccessTokenJsonExtractorTest.java index c39b265a8..6628d1d9b 100644 --- a/scribejava-core/src/test/java/com/github/scribejava/core/extractors/OAuth2AccessTokenJsonExtractorTest.java +++ b/scribejava-core/src/test/java/com/github/scribejava/core/extractors/OAuth2AccessTokenJsonExtractorTest.java @@ -10,7 +10,8 @@ import java.util.Collections; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.Assert.assertThrows; +import org.junit.function.ThrowingRunnable; public class OAuth2AccessTokenJsonExtractorTest { @@ -62,18 +63,26 @@ public void shouldParseScopeFromResponse() throws IOException { assertEquals("refresh_token1", token3.getRefreshToken()); } - @Test(expected = IllegalArgumentException.class) public void shouldThrowExceptionIfForNullParameters() throws IOException { try (Response response = ok(null)) { - extractor.extract(response); + assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(response); + } + }); } } - @Test(expected = IllegalArgumentException.class) public void shouldThrowExceptionIfForEmptyStrings() throws IOException { final String responseBody = ""; try (Response response = ok(responseBody)) { - extractor.extract(response); + assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(response); + } + }); } } @@ -84,9 +93,13 @@ public void shouldThrowExceptionIfResponseIsError() throws IOException { + "\"error\":\"invalid_grant\"" + "}"; try (Response response = error(responseBody)) { - extractor.extract(response); - fail(); - } catch (OAuth2AccessTokenErrorResponse oaer) { + final OAuth2AccessTokenErrorResponse oaer = assertThrows(OAuth2AccessTokenErrorResponse.class, + new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(response); + } + }); assertEquals(OAuth2Error.INVALID_GRANT, oaer.getError()); assertEquals("unknown, invalid, or expired refresh token", oaer.getErrorDescription()); } diff --git a/scribejava-core/src/test/java/com/github/scribejava/core/model/OAuthRequestTest.java b/scribejava-core/src/test/java/com/github/scribejava/core/model/OAuthRequestTest.java index 03688ce16..32d5d6731 100644 --- a/scribejava-core/src/test/java/com/github/scribejava/core/model/OAuthRequestTest.java +++ b/scribejava-core/src/test/java/com/github/scribejava/core/model/OAuthRequestTest.java @@ -1,9 +1,11 @@ package com.github.scribejava.core.model; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import org.junit.Before; import org.junit.Test; +import org.junit.function.ThrowingRunnable; public class OAuthRequestTest { @@ -25,9 +27,13 @@ public void shouldAddOAuthParamters() { assertEquals(5, request.getOauthParameters().size()); } - @Test(expected = IllegalArgumentException.class) public void shouldThrowExceptionIfParameterIsNotOAuth() { - request.addOAuthParameter("otherParam", "value"); + assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + request.addOAuthParameter("otherParam", "value"); + } + }); } @Test diff --git a/scribejava-core/src/test/java/com/github/scribejava/core/model/ParameterListTest.java b/scribejava-core/src/test/java/com/github/scribejava/core/model/ParameterListTest.java index 78a22c9f9..702c66395 100644 --- a/scribejava-core/src/test/java/com/github/scribejava/core/model/ParameterListTest.java +++ b/scribejava-core/src/test/java/com/github/scribejava/core/model/ParameterListTest.java @@ -5,6 +5,8 @@ import org.junit.Test; import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertThrows; +import org.junit.function.ThrowingRunnable; public class ParameterListTest { @@ -15,9 +17,13 @@ public void setUp() { this.params = new ParameterList(); } - @Test(expected = IllegalArgumentException.class) public void shouldThrowExceptionWhenAppendingNullMapToQuerystring() { - params.appendTo(null); + assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + params.appendTo(null); + } + }); } @Test diff --git a/scribejava-core/src/test/java/com/github/scribejava/core/oauth/OAuth20ServiceTest.java b/scribejava-core/src/test/java/com/github/scribejava/core/oauth/OAuth20ServiceTest.java index 7dfe361f0..3e0024c6c 100644 --- a/scribejava-core/src/test/java/com/github/scribejava/core/oauth/OAuth20ServiceTest.java +++ b/scribejava-core/src/test/java/com/github/scribejava/core/oauth/OAuth20ServiceTest.java @@ -2,8 +2,8 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.scribejava.core.base64.Base64; import com.github.scribejava.core.builder.ServiceBuilder; -import com.github.scribejava.core.java8.Base64; import com.github.scribejava.core.model.OAuth2AccessToken; import com.github.scribejava.core.model.OAuth2Authorization; import com.github.scribejava.core.model.OAuthConstants; @@ -18,7 +18,6 @@ public class OAuth20ServiceTest { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - private final Base64.Encoder base64Encoder = Base64.getEncoder(); @Test public void shouldProduceCorrectRequestSync() throws IOException, InterruptedException, ExecutionException { @@ -34,7 +33,7 @@ public void shouldProduceCorrectRequestSync() throws IOException, InterruptedExc assertEquals(OAuth20ServiceUnit.TOKEN, response.get(OAuthConstants.ACCESS_TOKEN).asText()); assertEquals(OAuth20ServiceUnit.EXPIRES, response.get("expires_in").asInt()); - final String authorize = base64Encoder.encodeToString( + final String authorize = Base64.encode( String.format("%s:%s", service.getApiKey(), service.getApiSecret()).getBytes(Charset.forName("UTF-8"))); assertEquals(OAuthConstants.BASIC + ' ' + authorize, response.get(OAuthConstants.HEADER).asText()); @@ -59,7 +58,7 @@ public void shouldProduceCorrectRequestAsync() throws ExecutionException, Interr assertEquals(OAuth20ServiceUnit.TOKEN, response.get(OAuthConstants.ACCESS_TOKEN).asText()); assertEquals(OAuth20ServiceUnit.EXPIRES, response.get("expires_in").asInt()); - final String authorize = base64Encoder.encodeToString( + final String authorize = Base64.encode( String.format("%s:%s", service.getApiKey(), service.getApiSecret()).getBytes(Charset.forName("UTF-8"))); assertEquals(OAuthConstants.BASIC + ' ' + authorize, response.get(OAuthConstants.HEADER).asText()); diff --git a/scribejava-core/src/test/java/com/github/scribejava/core/services/HMACSha1SignatureServiceTest.java b/scribejava-core/src/test/java/com/github/scribejava/core/services/HMACSha1SignatureServiceTest.java index d3fba9ae2..d824d2112 100644 --- a/scribejava-core/src/test/java/com/github/scribejava/core/services/HMACSha1SignatureServiceTest.java +++ b/scribejava-core/src/test/java/com/github/scribejava/core/services/HMACSha1SignatureServiceTest.java @@ -4,6 +4,8 @@ import org.junit.Before; import org.junit.Test; import com.github.scribejava.core.exceptions.OAuthException; +import static org.junit.Assert.assertThrows; +import org.junit.function.ThrowingRunnable; public class HMACSha1SignatureServiceTest { @@ -29,23 +31,34 @@ public void shouldReturnSignature() { assertEquals(signature, service.getSignature(baseString, apiSecret, tokenSecret)); } - @Test(expected = OAuthException.class) public void shouldThrowExceptionIfBaseStringIsNull() { - service.getSignature(null, "apiSecret", "tokenSecret"); + assertThrows(OAuthException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + service.getSignature(null, "apiSecret", "tokenSecret"); + } + }); } - @Test(expected = OAuthException.class) public void shouldThrowExceptionIfBaseStringIsEmpty() { - service.getSignature(" ", "apiSecret", "tokenSecret"); + assertThrows(OAuthException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + service.getSignature(" ", "apiSecret", "tokenSecret"); + } + }); } - @Test(expected = OAuthException.class) public void shouldThrowExceptionIfApiSecretIsNull() { - service.getSignature("base string", null, "tokenSecret"); + assertThrows(OAuthException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + service.getSignature("base string", null, "tokenSecret"); + } + }); } - @Test(expected = OAuthException.class) - public void shouldThrowExceptionIfApiSecretIsEmpty() { + public void shouldNotThrowExceptionIfApiSecretIsEmpty() { service.getSignature("base string", " ", "tokenSecret"); } } diff --git a/scribejava-core/src/test/java/com/github/scribejava/core/services/RSASha1SignatureServiceTest.java b/scribejava-core/src/test/java/com/github/scribejava/core/services/RSASha1SignatureServiceTest.java index adf93c68f..652f328e3 100644 --- a/scribejava-core/src/test/java/com/github/scribejava/core/services/RSASha1SignatureServiceTest.java +++ b/scribejava-core/src/test/java/com/github/scribejava/core/services/RSASha1SignatureServiceTest.java @@ -1,11 +1,11 @@ package com.github.scribejava.core.services; -import com.github.scribejava.core.java8.Base64; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; +import org.apache.commons.codec.binary.Base64; import static org.junit.Assert.assertEquals; import org.junit.Test; @@ -53,7 +53,7 @@ private static PrivateKey getPrivateKey() { try { final KeyFactory fac = KeyFactory.getInstance("RSA"); - final PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(Base64.getMimeDecoder().decode(str)); + final PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(str)); return fac.generatePrivate(privKeySpec); } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { throw new RuntimeException(e); diff --git a/scribejava-core/src/test/java/com/github/scribejava/core/utils/OAuthEncoderTest.java b/scribejava-core/src/test/java/com/github/scribejava/core/utils/OAuthEncoderTest.java index 1a1489b41..9a27d238d 100644 --- a/scribejava-core/src/test/java/com/github/scribejava/core/utils/OAuthEncoderTest.java +++ b/scribejava-core/src/test/java/com/github/scribejava/core/utils/OAuthEncoderTest.java @@ -1,7 +1,9 @@ package com.github.scribejava.core.utils; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; import org.junit.Test; +import org.junit.function.ThrowingRunnable; public class OAuthEncoderTest { @@ -34,14 +36,22 @@ public void shouldNotPercentEncodeReservedCharacters() { assertEquals(encoded, OAuthEncoder.encode(plain)); } - @Test(expected = IllegalArgumentException.class) public void shouldThrowExceptionIfStringToEncodeIsNull() { - OAuthEncoder.encode(null); + assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + OAuthEncoder.encode(null); + } + }); } - @Test(expected = IllegalArgumentException.class) public void shouldThrowExceptionIfStringToDecodeIsNull() { - OAuthEncoder.decode(null); + assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + OAuthEncoder.decode(null); + } + }); } @Test diff --git a/scribejava-core/src/test/java/com/github/scribejava/core/utils/PreconditionsTest.java b/scribejava-core/src/test/java/com/github/scribejava/core/utils/PreconditionsTest.java index ec6722c5a..1e3bd71e2 100644 --- a/scribejava-core/src/test/java/com/github/scribejava/core/utils/PreconditionsTest.java +++ b/scribejava-core/src/test/java/com/github/scribejava/core/utils/PreconditionsTest.java @@ -1,28 +1,45 @@ package com.github.scribejava.core.utils; -import org.junit.Test; +import static org.junit.Assert.assertThrows; +import org.junit.function.ThrowingRunnable; public class PreconditionsTest { private static final String ERROR_MSG = ""; - @Test(expected = IllegalArgumentException.class) public void shouldThrowExceptionForNullObjects() { - Preconditions.checkNotNull(null, ERROR_MSG); + assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + Preconditions.checkNotNull(null, ERROR_MSG); + } + }); } - @Test(expected = IllegalArgumentException.class) public void shouldThrowExceptionForNullStrings() { - Preconditions.checkEmptyString(null, ERROR_MSG); + assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + Preconditions.checkEmptyString(null, ERROR_MSG); + } + }); } - @Test(expected = IllegalArgumentException.class) public void shouldThrowExceptionForEmptyStrings() { - Preconditions.checkEmptyString("", ERROR_MSG); + assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + Preconditions.checkEmptyString("", ERROR_MSG); + } + }); } - @Test(expected = IllegalArgumentException.class) public void shouldThrowExceptionForSpacesOnlyStrings() { - Preconditions.checkEmptyString(" ", ERROR_MSG); + assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + Preconditions.checkEmptyString(" ", ERROR_MSG); + } + }); } } diff --git a/scribejava-core/src/test/java/com/github/scribejava/core/utils/StreamUtilsTest.java b/scribejava-core/src/test/java/com/github/scribejava/core/utils/StreamUtilsTest.java index cb5c6c7c9..61937bac6 100644 --- a/scribejava-core/src/test/java/com/github/scribejava/core/utils/StreamUtilsTest.java +++ b/scribejava-core/src/test/java/com/github/scribejava/core/utils/StreamUtilsTest.java @@ -4,8 +4,9 @@ import java.io.IOException; import java.io.InputStream; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.junit.Assert.assertThrows; import org.junit.Test; +import org.junit.function.ThrowingRunnable; public class StreamUtilsTest { @@ -27,16 +28,22 @@ public void shouldCorrectlyDecodeAStream() throws IOException { assertEquals("expected", decoded); } - @Test(expected = IllegalArgumentException.class) public void shouldFailForNullParameter() throws IOException { - StreamUtils.getStreamContents(null); - fail("Must throw exception before getting here"); + assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + StreamUtils.getStreamContents(null); + } + }); } - @Test(expected = IOException.class) public void shouldFailWithBrokenStream() throws IOException { - // This object simulates problems with input stream. - StreamUtils.getStreamContents(ALLWAYS_ERROR_INPUT_STREAM); - fail("Must throw exception before getting here"); + assertThrows(IOException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + // This object simulates problems with input stream. + StreamUtils.getStreamContents(ALLWAYS_ERROR_INPUT_STREAM); + } + }); } } diff --git a/scribejava-httpclient-ahc/pom.xml b/scribejava-httpclient-ahc/pom.xml index 358e8e73f..fabd225c3 100644 --- a/scribejava-httpclient-ahc/pom.xml +++ b/scribejava-httpclient-ahc/pom.xml @@ -5,7 +5,7 @@ com.github.scribejava scribejava - 8.0.0 + 8.3.4-SNAPSHOT ../pom.xml @@ -20,10 +20,16 @@ scribejava-core ${project.version} + + org.slf4j + slf4j-simple + 2.0.3 + test + org.asynchttpclient async-http-client - 2.12.1 + 2.12.3 com.github.scribejava diff --git a/scribejava-httpclient-ahc/src/main/java/com/github/scribejava/httpclient/ahc/AhcHttpClient.java b/scribejava-httpclient-ahc/src/main/java/com/github/scribejava/httpclient/ahc/AhcHttpClient.java index 4da925590..5c9b87249 100644 --- a/scribejava-httpclient-ahc/src/main/java/com/github/scribejava/httpclient/ahc/AhcHttpClient.java +++ b/scribejava-httpclient-ahc/src/main/java/com/github/scribejava/httpclient/ahc/AhcHttpClient.java @@ -2,7 +2,6 @@ import com.github.scribejava.core.httpclient.AbstractAsyncOnlyHttpClient; import com.github.scribejava.core.httpclient.multipart.MultipartPayload; -import com.github.scribejava.core.java8.Consumer; import com.github.scribejava.core.model.OAuthAsyncRequestCallback; import com.github.scribejava.core.model.OAuthConstants; import com.github.scribejava.core.model.OAuthRequest; @@ -43,7 +42,7 @@ public void close() throws IOException { @Override public Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, byte[] bodyContents, OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter) { - return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, new ByteArrayConsumer(bodyContents), callback, + return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, BodySetter.BYTE_ARRAY, bodyContents, callback, converter); } @@ -58,19 +57,19 @@ public Future executeAsync(String userAgent, Map headers, @Override public Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, String bodyContents, OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter) { - return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, new StringConsumer(bodyContents), callback, + return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, BodySetter.STRING, bodyContents, callback, converter); } @Override public Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, File bodyContents, OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter) { - return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, new FileConsumer(bodyContents), callback, + return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, BodySetter.FILE, bodyContents, callback, converter); } private Future doExecuteAsync(String userAgent, Map headers, Verb httpVerb, - String completeUrl, Consumer bodySetter, OAuthAsyncRequestCallback callback, + String completeUrl, BodySetter bodySetter, Object bodyContents, OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter) { final BoundRequestBuilder boundRequestBuilder; switch (httpVerb) { @@ -94,7 +93,7 @@ private Future doExecuteAsync(String userAgent, Map heade if (!headers.containsKey(CONTENT_TYPE)) { boundRequestBuilder.addHeader(CONTENT_TYPE, DEFAULT_CONTENT_TYPE); } - bodySetter.accept(boundRequestBuilder); + bodySetter.setBody(boundRequestBuilder, bodyContents); } for (Map.Entry header : headers.entrySet()) { @@ -108,45 +107,26 @@ private Future doExecuteAsync(String userAgent, Map heade return boundRequestBuilder.execute(new OAuthAsyncCompletionHandler<>(callback, converter)); } - private static class ByteArrayConsumer implements Consumer { - - private final byte[] bodyContents; - - private ByteArrayConsumer(byte[] bodyContents) { - this.bodyContents = bodyContents; - } - - @Override - public void accept(BoundRequestBuilder requestBuilder) { - requestBuilder.setBody(bodyContents); - } - } - - private static class StringConsumer implements Consumer { - - private final String bodyContents; - - private StringConsumer(String bodyContents) { - this.bodyContents = bodyContents; - } - - @Override - public void accept(BoundRequestBuilder requestBuilder) { - requestBuilder.setBody(bodyContents); - } - } - - private static class FileConsumer implements Consumer { - - private final File bodyContents; - - private FileConsumer(File bodyContents) { - this.bodyContents = bodyContents; - } + private enum BodySetter { + BYTE_ARRAY { + @Override + BoundRequestBuilder setBody(BoundRequestBuilder requestBuilder, Object bodyContents) { + return requestBuilder.setBody((byte[]) bodyContents); + } + }, + STRING { + @Override + BoundRequestBuilder setBody(BoundRequestBuilder requestBuilder, Object bodyContents) { + return requestBuilder.setBody((String) bodyContents); + } + }, + FILE { + @Override + BoundRequestBuilder setBody(BoundRequestBuilder requestBuilder, Object bodyContents) { + return requestBuilder.setBody((File) bodyContents); + } + }; - @Override - public void accept(BoundRequestBuilder requestBuilder) { - requestBuilder.setBody(bodyContents); - } + abstract BoundRequestBuilder setBody(BoundRequestBuilder requestBuilder, Object bodyContents); } } diff --git a/scribejava-httpclient-apache/pom.xml b/scribejava-httpclient-apache/pom.xml index fa367ff4a..1dc9dd53c 100644 --- a/scribejava-httpclient-apache/pom.xml +++ b/scribejava-httpclient-apache/pom.xml @@ -5,7 +5,7 @@ com.github.scribejava scribejava - 8.0.0 + 8.3.4-SNAPSHOT ../pom.xml @@ -28,7 +28,7 @@ org.apache.httpcomponents httpasyncclient - 4.1.4 + 4.1.5 com.github.scribejava diff --git a/scribejava-httpclient-apache/src/test/java/com/github/scribejava/httpclient/apache/OAuthAsyncCompletionHandlerTest.java b/scribejava-httpclient-apache/src/test/java/com/github/scribejava/httpclient/apache/OAuthAsyncCompletionHandlerTest.java index e1a4ddc9c..684ac5c62 100644 --- a/scribejava-httpclient-apache/src/test/java/com/github/scribejava/httpclient/apache/OAuthAsyncCompletionHandlerTest.java +++ b/scribejava-httpclient-apache/src/test/java/com/github/scribejava/httpclient/apache/OAuthAsyncCompletionHandlerTest.java @@ -4,7 +4,6 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -23,6 +22,8 @@ import com.github.scribejava.core.model.OAuthAsyncRequestCallback; import com.github.scribejava.core.model.OAuthRequest; import com.github.scribejava.core.model.Response; +import static org.junit.Assert.assertThrows; +import org.junit.function.ThrowingRunnable; public class OAuthAsyncCompletionHandlerTest { @@ -80,7 +81,7 @@ public void shouldReleaseLatchOnSuccess() throws Exception { } @Test - public void shouldReleaseLatchOnIOException() throws Exception { + public void shouldReleaseLatchOnIOException() { handler = new OAuthAsyncCompletionHandler<>(callback, EXCEPTION_RESPONSE_CONVERTER); final HttpResponse response = new BasicHttpResponse(new BasicStatusLine(new ProtocolVersion("4", 1, 1), 200, "ok")); @@ -92,16 +93,16 @@ public void shouldReleaseLatchOnIOException() throws Exception { assertNotNull(callback.getThrowable()); assertTrue(callback.getThrowable() instanceof IOException); // verify latch is released - try { - handler.getResult(); - fail(); - } catch (ExecutionException expected) { - // expected - } + assertThrows(ExecutionException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + handler.getResult(); + } + }); } @Test - public void shouldReportOAuthException() throws Exception { + public void shouldReportOAuthException() { handler = new OAuthAsyncCompletionHandler<>(callback, OAUTH_EXCEPTION_RESPONSE_CONVERTER); final HttpResponse response = new BasicHttpResponse(new BasicStatusLine(new ProtocolVersion("4", 1, 1), 200, "ok")); @@ -113,16 +114,16 @@ public void shouldReportOAuthException() throws Exception { assertNotNull(callback.getThrowable()); assertTrue(callback.getThrowable() instanceof OAuthException); // verify latch is released - try { - handler.getResult(); - fail(); - } catch (ExecutionException expected) { - // expected - } + assertThrows(ExecutionException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + handler.getResult(); + } + }); } @Test - public void shouldReleaseLatchOnCancel() throws Exception { + public void shouldReleaseLatchOnCancel() { handler = new OAuthAsyncCompletionHandler<>(callback, ALL_GOOD_RESPONSE_CONVERTER); final HttpResponse response = new BasicHttpResponse(new BasicStatusLine(new ProtocolVersion("4", 1, 1), 200, "ok")); @@ -134,16 +135,16 @@ public void shouldReleaseLatchOnCancel() throws Exception { assertNotNull(callback.getThrowable()); assertTrue(callback.getThrowable() instanceof CancellationException); // verify latch is released - try { - handler.getResult(); - fail(); - } catch (ExecutionException expected) { - // expected - } + assertThrows(ExecutionException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + handler.getResult(); + } + }); } @Test - public void shouldReleaseLatchOnFailure() throws Exception { + public void shouldReleaseLatchOnFailure() { handler = new OAuthAsyncCompletionHandler<>(callback, ALL_GOOD_RESPONSE_CONVERTER); final HttpResponse response = new BasicHttpResponse(new BasicStatusLine(new ProtocolVersion("4", 1, 1), 200, "ok")); @@ -155,12 +156,12 @@ public void shouldReleaseLatchOnFailure() throws Exception { assertNotNull(callback.getThrowable()); assertTrue(callback.getThrowable() instanceof RuntimeException); // verify latch is released - try { - handler.getResult(); - fail(); - } catch (ExecutionException expected) { - // expected - } + assertThrows(ExecutionException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + handler.getResult(); + } + }); } private static class AllGoodResponseConverter implements OAuthRequest.ResponseConverter { diff --git a/scribejava-httpclient-armeria/pom.xml b/scribejava-httpclient-armeria/pom.xml index 074345d6a..54e58c619 100644 --- a/scribejava-httpclient-armeria/pom.xml +++ b/scribejava-httpclient-armeria/pom.xml @@ -5,7 +5,7 @@ com.github.scribejava scribejava - 8.0.0 + 8.3.4-SNAPSHOT ../pom.xml @@ -20,10 +20,16 @@ scribejava-core ${project.version} + + org.slf4j + slf4j-simple + 2.0.3 + test + com.linecorp.armeria armeria - 1.2.0 + 1.20.2 com.github.scribejava diff --git a/scribejava-httpclient-ning/pom.xml b/scribejava-httpclient-ning/pom.xml index 08971a209..39696d698 100644 --- a/scribejava-httpclient-ning/pom.xml +++ b/scribejava-httpclient-ning/pom.xml @@ -5,10 +5,10 @@ com.github.scribejava scribejava - 8.0.0 + 8.3.4-SNAPSHOT ../pom.xml - + com.github.scribejava scribejava-httpclient-ning ScribeJava Ning Async Http Client support @@ -20,6 +20,12 @@ scribejava-core ${project.version} + + org.slf4j + slf4j-simple + 2.0.3 + test + com.ning async-http-client diff --git a/scribejava-httpclient-ning/src/main/java/com/github/scribejava/httpclient/ning/NingHttpClient.java b/scribejava-httpclient-ning/src/main/java/com/github/scribejava/httpclient/ning/NingHttpClient.java index cc8c6fcda..52250f698 100644 --- a/scribejava-httpclient-ning/src/main/java/com/github/scribejava/httpclient/ning/NingHttpClient.java +++ b/scribejava-httpclient-ning/src/main/java/com/github/scribejava/httpclient/ning/NingHttpClient.java @@ -2,7 +2,6 @@ import com.github.scribejava.core.httpclient.AbstractAsyncOnlyHttpClient; import com.github.scribejava.core.httpclient.multipart.MultipartPayload; -import com.github.scribejava.core.java8.Consumer; import com.github.scribejava.core.model.OAuthAsyncRequestCallback; import com.github.scribejava.core.model.OAuthConstants; import com.github.scribejava.core.model.OAuthRequest; @@ -50,7 +49,7 @@ public Future executeAsync(String userAgent, Map headers, final byte[] bodyContents, OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter) { - return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, new ByteArrayConsumer(bodyContents), callback, + return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, BodySetter.BYTE_ARRAY, bodyContents, callback, converter); } @@ -67,7 +66,7 @@ public Future executeAsync(String userAgent, Map headers, final String bodyContents, OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter) { - return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, new StringConsumer(bodyContents), callback, + return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, BodySetter.STRING, bodyContents, callback, converter); } @@ -76,13 +75,13 @@ public Future executeAsync(String userAgent, Map headers, final File bodyContents, OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter) { - return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, new FileConsumer(bodyContents), callback, + return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, BodySetter.FILE, bodyContents, callback, converter); } private Future doExecuteAsync(String userAgent, Map headers, Verb httpVerb, - String completeUrl, Consumer bodySetter, - OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter) { + String completeUrl, BodySetter bodySetter, Object bodyContents, OAuthAsyncRequestCallback callback, + OAuthRequest.ResponseConverter converter) { final AsyncHttpClient.BoundRequestBuilder boundRequestBuilder; switch (httpVerb) { case GET: @@ -105,7 +104,7 @@ private Future doExecuteAsync(String userAgent, Map heade if (!headers.containsKey(CONTENT_TYPE)) { boundRequestBuilder.addHeader(CONTENT_TYPE, DEFAULT_CONTENT_TYPE); } - bodySetter.accept(boundRequestBuilder); + bodySetter.setBody(boundRequestBuilder, bodyContents); } for (Map.Entry header : headers.entrySet()) { @@ -119,45 +118,30 @@ private Future doExecuteAsync(String userAgent, Map heade return boundRequestBuilder.execute(new OAuthAsyncCompletionHandler<>(callback, converter)); } - private static class ByteArrayConsumer implements Consumer { - - private final byte[] bodyContents; - - private ByteArrayConsumer(byte[] bodyContents) { - this.bodyContents = bodyContents; - } - - @Override - public void accept(AsyncHttpClient.BoundRequestBuilder requestBuilder) { - requestBuilder.setBody(bodyContents); - } - } - - private static class StringConsumer implements Consumer { - - private final String bodyContents; - - private StringConsumer(String bodyContents) { - this.bodyContents = bodyContents; - } - - @Override - public void accept(AsyncHttpClient.BoundRequestBuilder requestBuilder) { - requestBuilder.setBody(bodyContents); - } - } - - private static class FileConsumer implements Consumer { - - private final File bodyContents; - - private FileConsumer(File bodyContents) { - this.bodyContents = bodyContents; - } + private enum BodySetter { + BYTE_ARRAY { + @Override + AsyncHttpClient.BoundRequestBuilder setBody(AsyncHttpClient.BoundRequestBuilder requestBuilder, + Object bodyContents) { + return requestBuilder.setBody((byte[]) bodyContents); + } + }, + STRING { + @Override + AsyncHttpClient.BoundRequestBuilder setBody(AsyncHttpClient.BoundRequestBuilder requestBuilder, + Object bodyContents) { + return requestBuilder.setBody((String) bodyContents); + } + }, + FILE { + @Override + AsyncHttpClient.BoundRequestBuilder setBody(AsyncHttpClient.BoundRequestBuilder requestBuilder, + Object bodyContents) { + return requestBuilder.setBody((File) bodyContents); + } + }; - @Override - public void accept(AsyncHttpClient.BoundRequestBuilder requestBuilder) { - requestBuilder.setBody(bodyContents); - } + abstract AsyncHttpClient.BoundRequestBuilder setBody(AsyncHttpClient.BoundRequestBuilder requestBuilder, + Object bodyContents); } } diff --git a/scribejava-httpclient-okhttp/pom.xml b/scribejava-httpclient-okhttp/pom.xml index 6d038e039..59bc2bffc 100644 --- a/scribejava-httpclient-okhttp/pom.xml +++ b/scribejava-httpclient-okhttp/pom.xml @@ -5,7 +5,7 @@ com.github.scribejava scribejava - 8.0.0 + 8.3.4-SNAPSHOT ../pom.xml @@ -23,7 +23,7 @@ com.squareup.okhttp3 okhttp - 4.9.0 + 4.10.0 com.github.scribejava diff --git a/scribejava-httpclient-okhttp/src/test/java/com/github/scribejava/httpclient/okhttp/OAuthAsyncCompletionHandlerTest.java b/scribejava-httpclient-okhttp/src/test/java/com/github/scribejava/httpclient/okhttp/OAuthAsyncCompletionHandlerTest.java index 6e9f88fe5..b58e8df93 100644 --- a/scribejava-httpclient-okhttp/src/test/java/com/github/scribejava/httpclient/okhttp/OAuthAsyncCompletionHandlerTest.java +++ b/scribejava-httpclient-okhttp/src/test/java/com/github/scribejava/httpclient/okhttp/OAuthAsyncCompletionHandlerTest.java @@ -4,7 +4,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.assertThrows; import java.io.IOException; import java.util.concurrent.ExecutionException; @@ -22,6 +22,7 @@ import okhttp3.Protocol; import okhttp3.Request; import okhttp3.ResponseBody; +import org.junit.function.ThrowingRunnable; public class OAuthAsyncCompletionHandlerTest { @@ -88,7 +89,7 @@ public void shouldReleaseLatchOnSuccess() throws Exception { } @Test - public void shouldReleaseLatchOnIOException() throws Exception { + public void shouldReleaseLatchOnIOException() { handler = new OAuthAsyncCompletionHandler<>(callback, EXCEPTION_RESPONSE_CONVERTER, future); call.enqueue(handler); @@ -105,16 +106,16 @@ public void shouldReleaseLatchOnIOException() throws Exception { assertNotNull(callback.getThrowable()); assertTrue(callback.getThrowable() instanceof IOException); // verify latch is released - try { - future.get(); - fail(); - } catch (ExecutionException expected) { - // expected - } + assertThrows(ExecutionException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + future.get(); + } + }); } @Test - public void shouldReportOAuthException() throws Exception { + public void shouldReportOAuthException() { handler = new OAuthAsyncCompletionHandler<>(callback, OAUTH_EXCEPTION_RESPONSE_CONVERTER, future); call.enqueue(handler); @@ -131,16 +132,16 @@ public void shouldReportOAuthException() throws Exception { assertNotNull(callback.getThrowable()); assertTrue(callback.getThrowable() instanceof OAuthException); // verify latch is released - try { - future.get(); - fail(); - } catch (ExecutionException expected) { - // expected - } + assertThrows(ExecutionException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + future.get(); + } + }); } @Test - public void shouldReleaseLatchOnCancel() throws Exception { + public void shouldReleaseLatchOnCancel() { handler = new OAuthAsyncCompletionHandler<>(callback, ALL_GOOD_RESPONSE_CONVERTER, future); call.enqueue(handler); @@ -149,16 +150,16 @@ public void shouldReleaseLatchOnCancel() throws Exception { assertNotNull(callback.getThrowable()); assertTrue(callback.getThrowable() instanceof IOException); // verify latch is released - try { - future.get(); - fail(); - } catch (ExecutionException expected) { - // expected - } + assertThrows(ExecutionException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + future.get(); + } + }); } @Test - public void shouldReleaseLatchOnFailure() throws Exception { + public void shouldReleaseLatchOnFailure() { handler = new OAuthAsyncCompletionHandler<>(callback, ALL_GOOD_RESPONSE_CONVERTER, future); call.enqueue(handler); @@ -167,12 +168,12 @@ public void shouldReleaseLatchOnFailure() throws Exception { assertNotNull(callback.getThrowable()); assertTrue(callback.getThrowable() instanceof IOException); // verify latch is released - try { - future.get(); - fail(); - } catch (ExecutionException expected) { - // expected - } + assertThrows(ExecutionException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + future.get(); + } + }); } private static class AllGoodResponseConverter implements OAuthRequest.ResponseConverter { diff --git a/scribejava-java8/pom.xml b/scribejava-java8/pom.xml new file mode 100644 index 000000000..b75f6d2de --- /dev/null +++ b/scribejava-java8/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + + + com.github.scribejava + scribejava + 8.3.4-SNAPSHOT + ../pom.xml + + + com.github.scribejava + scribejava-java8 + ScribeJava Java 8+ compatibility stuff + jar + + + + + org.apache.felix + maven-bundle-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + + + + 8 + + diff --git a/scribejava-java8/src/main/java/com/github/scribejava/java8/base64/Java8Base64.java b/scribejava-java8/src/main/java/com/github/scribejava/java8/base64/Java8Base64.java new file mode 100644 index 000000000..eb391dcad --- /dev/null +++ b/scribejava-java8/src/main/java/com/github/scribejava/java8/base64/Java8Base64.java @@ -0,0 +1,17 @@ +package com.github.scribejava.java8.base64; + +public class Java8Base64 { + + private static final java.util.Base64.Encoder BASE64_ENCODER = java.util.Base64.getEncoder(); + private static final java.util.Base64.Encoder BASE64_URL_ENCODER_WITHOUT_PADDING + = java.util.Base64.getUrlEncoder().withoutPadding(); + + public String internalEncode(byte[] bytes) { + return BASE64_ENCODER.encodeToString(bytes); + } + + public String internalEncodeUrlWithoutPadding(byte[] bytes) { + return BASE64_URL_ENCODER_WITHOUT_PADDING.encodeToString(bytes); + } + +}