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/.travis.yml b/.travis.yml deleted file mode 100644 index a79954ad5..000000000 --- a/.travis.yml +++ /dev/null @@ -1,8 +0,0 @@ -language: java -script: mvn clean package -jdk: - - oraclejdk8 - - oraclejdk7 - - openjdk7 -os: - - linux diff --git a/LICENSE.txt b/LICENSE.txt index 8cde51fa9..6085098bc 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,5 @@ The MIT License -Copyright (c) 2013 hh.ru Copyright (c) 2010 Pablo Fernandez Permission is hereby granted, free of charge, to any person obtaining a copy @@ -19,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file +THE SOFTWARE. diff --git a/README.md b/README.md index 087c0f430..b90ef2e5f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # Welcome to the home of ScribeJava, the simple OAuth client Java lib! -[![Build Status](https://travis-ci.org/scribejava/scribejava.svg?branch=master)](https://travis-ci.org/scribejava/scribejava) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.scribejava/scribejava/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.scribejava/scribejava) +[![Donate](https://www.paypalobjects.com/en_US/RU/i/btn/btn_donateCC_LG.gif)](https://github.com/scribejava/scribejava/blob/master/donate.md) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.scribejava/scribejava/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.scribejava/scribejava) # Why use ScribeJava? @@ -11,70 +10,108 @@ 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()); ``` That **single line** (added newlines for readability) is the only thing you need to configure ScribeJava with LinkedIn's OAuth API for example. -Working runnable examples are [here](https://github.com/scribejava/scribejava/tree/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples) +Working executable examples are [here](https://github.com/scribejava/scribejava/tree/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples) +Common usage: [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20Example.java) ### Threadsafe Hit ScribeJava as hard and with many threads as you like. +### Java 7 compatible + +That's it. You can use it in old environments and in android apps. +note: To compile from sources you will need Java 9 or newer + ### Async and other HTTP clients ScribeJava support out-of-box several HTTP clients: - * ning async http client 1.9.x (maven module scribejava-httpclient-ning) - * asynchttpclient 2.x (maven module scribejava-httpclient-ahc) - * OkHttp (maven module scribejava-httpclient-okhttp) - - just add corresponding maven modules to your pom + * ning async http client 1.9.x (maven module scribejava-httpclient-ning) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FacebookAsyncNingExample.java) + * Async Http Client asynchttpclient 2.x (maven module scribejava-httpclient-ahc) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20AsyncAHCExample.java) + * OkHttp (maven module scribejava-httpclient-okhttp) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/GitHubAsyncOkHttpExample.java) + * Apache HttpComponents HttpClient (maven module scribejava-httpclient-apache) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FacebookAsyncApacheExample.java) + * Armeria HTTP client (required >= java 8) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20ArmeriaExample.java) + * any externally created HTTP client [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteExternalHttpExample.java) -### Supports all major 1.0a and 2.0 OAuth APIs out-of-the-box + just add corresponding maven modules to your pom -* AWeber (http://www.aweber.com/) -* Box (https://www.box.com/) -* Digg (http://digg.com/) +### Supports many flows and additional features + + * [RFC 6749](https://tools.ietf.org/html/rfc6749) The OAuth 2.0 Authorization Framework, [Authorization Code Authorization Grant](https://tools.ietf.org/html/rfc6749#section-4.1), [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20Example.java) + * [RFC 6749](https://tools.ietf.org/html/rfc6749) The OAuth 2.0 Authorization Framework, [Resource Owner Password Credentials Authorization Grant](https://tools.ietf.org/html/rfc6749#section-4.3) + * [RFC 6749](https://tools.ietf.org/html/rfc6749) The OAuth 2.0 Authorization Framework, [Client Credentials Authorization Grant](https://tools.ietf.org/html/rfc6749#section-4.4), [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteClientCredentialsGrantExample.java) + * [RFC 6749](https://tools.ietf.org/html/rfc6749) The OAuth 2.0 Authorization Framework, [Refreshing an Access Token](https://tools.ietf.org/html/rfc6749#section-6), [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20Example.java#L77) + * [RFC 6750](https://tools.ietf.org/html/rfc6750) The OAuth 2.0 Authorization Framework: Bearer Token Usage + * [RFC 7636](https://tools.ietf.org/html/rfc7636) Proof Key for Code Exchange by OAuth Public Clients (PKCE), [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20WithPKCEExample.java) + * [RFC 7009](https://tools.ietf.org/html/rfc7009) OAuth 2.0 Token Revocation, [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20RevokeExample.java) + * [RFC 8628](https://tools.ietf.org/html/rfc8628) OAuth 2.0 Device Authorization Grant [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20DeviceAuthorizationGrantExample.java) + * [RFC 5849](https://tools.ietf.org/html/rfc5849) The OAuth 1.0 Protocol, [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TwitterExample.java) + +### Supports all (50+) major 1.0a and 2.0 OAuth APIs out-of-the-box + +* Asana (https://asana.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/AsanaExample.java) +* Automatic (https://www.automatic.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/AutomaticExample.java) +* AWeber (http://www.aweber.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/AWeberExample.java) +* Box (https://www.box.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Box20Example.java) +* Dataporten (https://docs.dataporten.no/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/DataportenExample.java) +* Digg (http://digg.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/DiggExample.java) +* Discord (https://discordapp.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/DiscordExample.java) * Доктор на работе (https://www.doktornarabote.ru/) -* Facebook (https://www.facebook.com/) -* Flickr (https://www.flickr.com/) -* Foursquare (https://foursquare.com/) -* Frappe (https://github.com/frappe/frappe) -* Freelancer (https://www.freelancer.com/) -* Genius (http://genius.com/) -* GitHub (https://github.com/) -* Google (https://www.google.com/) -* HeadHunter ХэдХантер (https://hh.ru/) -* Imgur (http://imgur.com/) -* Kaixin 开心网 (http://www.kaixin001.com/) -* LinkedIn (https://www.linkedin.com/) -* Microsoft Live (https://login.live.com/) -* Mail.Ru (https://mail.ru/) -* Meetup (http://www.meetup.com/) -* NAVER (http://www.naver.com/) -* NetEase (http://www.163.com/) -* Odnoklassniki Одноклассники (http://ok.ru/) -* Pinterest (https://www.pinterest.com/) -* 500px (https://500px.com/) -* Renren (http://renren.com/) -* Salesforce (https://www.salesforce.com/) -* Sina (http://www.sina.com.cn/ http://weibo.com/login.php) -* Skyrock (http://skyrock.com/) -* sohu 搜狐 (http://www.sohu.com/) -* StackExchange (http://stackexchange.com/) -* The Things Network (v1-staging and v2-preview) (https://www.thethingsnetwork.org/) -* Trello (https://trello.com/) -* Tumblr (https://www.tumblr.com/) -* TUT.BY (http://www.tut.by/) -* Twitter (https://twitter.com/) -* Viadeo (http://viadeo.com/) -* VK ВКонтакте (http://vk.com/) -* XING (https://www.xing.com/) -* Yahoo (https://www.yahoo.com/) -* Misfit (http://misfit.com/) +* Dropbox (https://www.dropbox.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/DropboxExample.java) +* Etsy (https://www.etsy.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/EtsyExample.java) +* Facebook (https://www.facebook.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FacebookExample.java), [example with Async Apache HTTP client](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FacebookAsyncApacheExample.java), [example with Async Ning HTTP client](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FacebookAsyncNingExample.java) +* Fitbit (https://www.fitbit.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FitbitApi20Example.java) +* Flickr (https://www.flickr.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FlickrExample.java) +* Foursquare (https://foursquare.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Foursquare2Example.java), [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FoursquareExample.java) +* Frappe (https://github.com/frappe/frappe) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FrappeExample.java) +* Freelancer (https://www.freelancer.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FreelancerExample.java) +* Genius (http://genius.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/GeniusExample.java) +* GitHub (https://github.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/GitHubExample.java), [example with OkHttp HTTP client](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/GitHubAsyncOkHttpExample.java) +* Google (https://www.google.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20Example.java), [example with Async Http Client](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20AsyncAHCExample.java), [example Revoke](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20RevokeExample.java), [example with PKCEE](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20WithPKCEExample.java) +* 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) +* LinkedIn (https://www.linkedin.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/LinkedIn20Example.java), [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/LinkedInExample.java), [example with custom scopes](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/LinkedInExampleWithScopes.java) +* Mail.Ru (https://mail.ru/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MailruExample.java), [example with Async Ning HTTP Client](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MailruAsyncExample.java) +* MediaWiki (https://www.mediawiki.org/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MediaWikiExample.java) +* Meetup (https://www.meetup.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Meetup20Example.java), [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MeetupExample.java) +* Microsoft Azure Active Directory (Azure AD) (http://azure.microsoft.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MicrosoftAzureActiveDirectoryExample.java) +* Microsoft Azure Active Directory (Azure AD) 2.0 (http://azure.microsoft.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MicrosoftAzureActiveDirectory20Example.java) +* Microsoft Live (https://login.live.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/LiveExample.java) +* Misfit (http://misfit.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MisfitExample.java) +* NAVER (http://www.naver.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/NaverExample.java) +* Odnoklassniki Одноклассники (http://ok.ru/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/OdnoklassnikiExample.java) +* Polar (https://www.polar.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/PolarAPIExample.java) +* Pinterest (https://www.pinterest.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/PinterestExample.java) +* 500px (https://500px.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Px500Example.java) +* Renren (http://renren.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/RenrenExample.java) +* 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) +* Tumblr (https://www.tumblr.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TumblrExample.java) +* TUT.BY (http://www.tut.by/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TutByExample.java) +* Twitter (https://twitter.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TwitterExample.java) +* uCoz (https://www.ucoz.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/UcozExample.java) +* Viadeo (http://viadeo.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/ViadeoExample.java) +* VK ВКонтакте (http://vk.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteExample.java), [example Client Credentials Grant](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteClientCredentialsGrantExample.java), [example with External HTTP Client](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteExternalHttpExample.java) +* Wunderlist (https://www.wunderlist.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/WunderlistExample.java) +* Xero (https://www.xero.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/XeroExample.java) +* XING (https://www.xing.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/XingExample.java) +* Yahoo (https://www.yahoo.com/) [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Yahoo20Example.java), [example](https://github.com/scribejava/scribejava/blob/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/YahooExample.java) * check the [examples folder](https://github.com/scribejava/scribejava/tree/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples) ### Small and modular @@ -100,7 +137,7 @@ You can pull ScribeJava from the central maven repository, just add these to you com.github.scribejava scribejava-apis - 4.1.2 + 8.3.3 ``` @@ -109,14 +146,29 @@ And in case you need just core classes (that's it, without any external API (FB, com.github.scribejava scribejava-core - 4.1.2 + 8.3.3 ``` +## How can I help ScribeJava + +First of all, Pull Requests are welcome, the second option is [donations](https://github.com/scribejava/scribejava/blob/master/donate.md). + +## When will ScribeJava support XXX (new RFC, custom functionality, new API etc.) + +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 (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 Check the [Getting Started](https://github.com/scribejava/scribejava/wiki/getting-started) page and start rocking! Please Read the [FAQ](https://github.com/scribejava/scribejava/wiki/faq) before creating an issue :) +Some useful info and answers you can find on the [wiki](https://github.com/scribejava/scribejava/wiki) + Also, remember to read the [fantastic tutorial](http://akoskm.github.io/2015/07/31/twitter-sign-in-for-web-apps.html) that [@akoskm](https://twitter.com/akoskm) wrote to easily integrate a server side app with an API (twitter in this case). ## Questions? diff --git a/changelog b/changelog index 01f18217c..e962e073b 100644 --- a/changelog +++ b/changelog @@ -1,6 +1,186 @@ -[SNAPSHOT] +[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) + * add support for OAuth 2.0 Device Authorization Grant (RFC 8628) (thanks to https://github.com/rebarbora-mckvak) + * update Google API URLs + +[7.1.1] + * add Proxy support (via config's option) to internal JDKHttpClient (thanks to https://github.com/bjournaud) + * fix typo (change "Verfier" to "Verifier") (thanks to https://github.com/afkbrb) + * fix Multipart support in JDKHttpClient (thanks to https://github.com/eos1d3) + +[7.0.0] + * Add Polar API (https://www.polar.com/) (thanks to https://github.com/vidi42) + * make Response accept resources to autoclose and autoclose it (thanks to https://github.com/drei01) + * fix url encoding in POST payload (it's needed for 'application/x-www-form-urlencoded' Content-Type) + + unit tests (thanks to https://github.com/max904-github) + * Add Armeria HTTP client (thanks to https://github.com/max904-github) + +[6.9.0] + * Add Xero API (https://www.xero.com/) (thanks to https://github.com/SidneyAllen) + +[6.8.1] + * make Response implements Closeable (thanks to https://github.com/omaric) + * fix Type resolution for builder pattern in ServiceBuilderOAuth10a (thanks to https://github.com/mgyucht) + * fix no Content-length errors (thanks to https://github.com/mikita-herasiutsin and https://github.com/iankurverma) + +[6.8.0] + * Add debug output to OAuth2Service (thanks to https://github.com/rbarbey) + * Add Dropbox API (https://www.dropbox.com/) (thanks to https://github.com/petrkopotev) + +[6.7.0] + * Add OAuth2 support for Meetup.com (thanks to https://github.com/stevedes77) + * upgrade okhttp to 4.0.1 and security fix for jackson-databind 2.9.9.1 + +[6.6.3] + * fix NPE for OpenId providers + +[6.6.2] + * add PMD checks on compile + * add all OAuth error codes from supported RFCs (incl. "invalid_token") (thanks to https://github.com/echorebel) + * Update LinkedIn Example to API v2 (thanks to https://github.com/peternees) + * switch to jackson dependency to parse json responses (thanks to https://github.com/galimru) + +[6.5.1] + * cleanup deprecates methods + +[6.5.0] + * separate OAuth1.0a and OAuth2.0 ServiceBuilders, + introduce AuthorizationUrlBuilder (along with deprecation of AuthorizationUrlWithPKCE) + add possibility to provide different scopes for each Access Token request + * upgrade Facebook API from v2.11 to v3.2 + * upgrade VkontakteApi from 5.73 to 5.92 + +[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) + * separate OAuth1.0a and OAuth2.0 classes + +[6.3.0] + * fix Muplipart request model and implement it for a jdk HTTP client (thanks to https://github.com/NTPape) + * remove any Google+ mention (switch to clean Google OAuth2) (thanks to https://github.com/fvasco) + * fix Microsoft Azure AD v1.0 and v2.0 (thanks to https://github.com/kenpusney and https://github.com/oscararias) + * add new API Asana (https://asana.com/) (thanks to https://github.com/joestazak) + * state param should be used only for authorization url generation, for v2 only, for Authorization Code Grant only, + 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) + +[6.1.0] + * add new API Keycloak (https://www.keycloak.org/) (thanks to https://github.com/JureZelic) + * add new API Discord (https://discordapp.com/) (thanks to https://github.com/Jokuni) + +[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 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) + * 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) + * 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) + * add HiOrg-Server (https://www.hiorg-server.de/) API (thanks to https://github.com/MartinBoehmer) + +[5.5.0] + * fix error parsing for Fitbit (thanks to https://github.com/danmana) + * optimize debug log performance impact on prod in OAuth1 and fix + NoClassDefFoundError on Android device with SDK 18 and lower (thanks to https://github.com/arcao) + * add new API - MediaWiki (https://www.mediawiki.org/) (thanks to https://github.com/lucaswerkmeister) + +[5.4.0] + * 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) + * deprecate OAuthConfig + * OAuth1.0: send "oob" instead of null callback while requesting RequestToken (thanks to https://github.com/Rafaelsk) + +[5.3.0] + * fix Salesforce API (thanks to https://github.com/jhorowitz-firedrum) + * remove 'final' from methods in OAuth[10a|20]Service to allow mocking it + * fix Pinterest API (thanks to https://github.com/sschwieb) + * add Yahoo2 API (thanks to https://github.com/javatestcase) + * fix Tumblr urls, convert to https (thanks to https://github.com/highthunder) + * fix: allow spaces in scope param in OAuth2Accesstoken response + * 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) + * java7 compatible again! + +[5.1.0] + * drop optional dependency on Apache commons-codec + * add API - Dataporten (https://docs.dataporten.no/) (thanks to https://github.com/xibriz) + * add API - Microsoft Azure Active Directory (Azure AD) (thanks to https://github.com/kaushalmall) + * fix LinkedInApi20 (thanks to https://github.com/jhorowitz-firedrum) + +[5.0.0] + * 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) + * 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) + * 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")) + +[4.2.0] * DELETE in JdkClient permits, but not requires payload (thanks to https://github.com/miguelD73) * add new API - Frappe (https://github.com/frappe/frappe) (thanks to https://github.com/revant) + * add new API - Etsy (https://www.etsy.com/) (thanks to https://github.com/efekocabas) [4.1.2] * LinkedIn use Header to sign OAuth2 requests @@ -8,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] @@ -35,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 @@ -51,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 @@ -99,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] @@ -142,8 +335,7 @@ [2.0] * merge back SubScribe fork to the ScribeJava - + for previous changes see v1-changelog - changelog for 1.x version v2pre-changelog - changelog for SubScribe fork - diff --git a/checkstyle.xml b/checkstyle.xml index 9cba55776..f7efbad65 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -45,7 +45,6 @@ - @@ -76,7 +75,6 @@ - @@ -95,20 +93,22 @@ - - - - - + + + + + diff --git a/donate.md b/donate.md new file mode 100644 index 000000000..99d3686dc --- /dev/null +++ b/donate.md @@ -0,0 +1,12 @@ +You can now help ScribeJava not only by Pull Requests. + +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 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
+2.Ian Strachan
+3.Your name can be here. diff --git a/pmd.xml b/pmd.xml new file mode 100644 index 000000000..5d93d340b --- /dev/null +++ b/pmd.xml @@ -0,0 +1,124 @@ + + + + + This ruleset defines the PMD rules for project "ScribeJava". + + + + + + + true + + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 15ee61cda..b484b6a71 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ com.github.scribejava scribejava pom - 4.1.3-SNAPSHOT + 8.3.4-SNAPSHOT ScribeJava OAuth Library The best OAuth library out there https://github.com/scribejava/scribejava @@ -16,10 +16,13 @@ scribejava-core + scribejava-java8 scribejava-apis scribejava-httpclient-ahc scribejava-httpclient-ning scribejava-httpclient-okhttp + scribejava-httpclient-apache + scribejava-httpclient-armeria @@ -30,45 +33,17 @@ - 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 - HEAD - + HEAD + kullfar - Stas Gromov - s.gromov@hh.ru - hh.ru - http://hh.ru - - all - - +3 - - kullfar@gmail.com - +7-909-677-11-16 - - - - chernatkin - Sergey Chernatkin - s.chernatkin@hh.ru - hh.ru - http://hh.ru - - all - - +3 - - - igaranina - Irina Garanina - i.garanina@hh.ru - hh.ru - http://hh.ru + Stanislav Gromov + kullfar@gmail.com all @@ -77,25 +52,23 @@ + + com.fasterxml.jackson.core + jackson-databind + 2.14.0 + junit junit - 4.12 + 4.13.2 test - com.google.code.gson - gson - 2.8.1 + com.squareup.okhttp3 + mockwebserver + 4.10.0 test - - commons-codec - commons-codec - 1.10 - compile - true - @@ -103,7 +76,7 @@ org.apache.felix maven-bundle-plugin - 3.3.0 + 5.1.8 bundle-manifest @@ -117,7 +90,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.0.2 + 3.3.0 ${project.build.outputDirectory}/META-INF/MANIFEST.MF @@ -127,38 +100,56 @@ org.apache.maven.plugins maven-checkstyle-plugin - 2.17 + 3.2.0 com.puppycrawl.tools checkstyle - 8.0 + 10.4 + org.apache.maven.plugins maven-release-plugin 2.5.3 true + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.2 + + + org.apache.maven.plugins + maven-clean-plugin + 3.2.0 + + + org.apache.maven.plugins + maven-install-plugin + 3.0.1 + maven-compiler-plugin - 3.6.1 + 3.10.1 UTF-8 - 1.7 - 1.7 + ${java.release} true + + -Xlint:-options + maven-deploy-plugin - 2.8.2 + 3.0.0 default-deploy @@ -172,7 +163,7 @@ org.apache.maven.plugins maven-resources-plugin - 3.0.2 + 3.3.0 UTF-8 @@ -180,7 +171,7 @@ org.apache.maven.plugins maven-source-plugin - 3.0.1 + 3.2.1 attach-sources @@ -193,9 +184,12 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.10.4 + 3.4.1 + ${java.home}/bin/javadoc UTF-8 + -html5 + all,-missing @@ -214,7 +208,9 @@ validate validate - ${basedir}/src + + ${basedir}/src + checkstyle.xml UTF-8 true @@ -225,34 +221,59 @@ + + org.apache.maven.plugins + maven-pmd-plugin + 3.19.0 + + + net.sourceforge.pmd + pmd-core + ${pmdVersion} + + + net.sourceforge.pmd + pmd-java + ${pmdVersion} + + + net.sourceforge.pmd + pmd-javascript + ${pmdVersion} + + + net.sourceforge.pmd + pmd-jsp + ${pmdVersion} + + + + 1.${java.release} + false + + ../pmd.xml + + true + true + true + + + + + check + + + + + + 7 + 6.51.0 + + - - jdk-1.7 - - 1.7 - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - 2.17 - - - com.puppycrawl.tools - checkstyle - 6.19 - - - - - - - release-sign-artifacts @@ -266,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 6993dd3e5..4e51bb8b5 100644 --- a/scribejava-apis/pom.xml +++ b/scribejava-apis/pom.xml @@ -5,10 +5,10 @@ com.github.scribejava scribejava - 4.1.3-SNAPSHOT + 8.3.4-SNAPSHOT ../pom.xml - + com.github.scribejava scribejava-apis ScribeJava APIs @@ -38,6 +38,24 @@ ${project.version} test + + com.github.scribejava + scribejava-httpclient-apache + ${project.version} + test + + + com.github.scribejava + scribejava-httpclient-armeria + ${project.version} + test + + + io.netty + netty-resolver + 4.1.84.Final + test + diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/AWeberApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/AWeberApi.java index fe5d86691..9ad3a185a 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/AWeberApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/AWeberApi.java @@ -1,11 +1,10 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi10a; -import com.github.scribejava.core.model.OAuth1RequestToken; public class AWeberApi extends DefaultApi10a { - private static final String AUTHORIZE_URL = "https://auth.aweber.com/1.0/oauth/authorize?oauth_token=%s"; + private static final String AUTHORIZE_URL = "https://auth.aweber.com/1.0/oauth/authorize"; private static final String REQUEST_TOKEN_ENDPOINT = "https://auth.aweber.com/1.0/oauth/request_token"; private static final String ACCESS_TOKEN_ENDPOINT = "https://auth.aweber.com/1.0/oauth/access_token"; @@ -13,6 +12,7 @@ protected AWeberApi() { } private static class InstanceHolder { + private static final AWeberApi INSTANCE = new AWeberApi(); } @@ -31,7 +31,7 @@ public String getRequestTokenEndpoint() { } @Override - public String getAuthorizationUrl(OAuth1RequestToken requestToken) { - return String.format(AUTHORIZE_URL, requestToken.getToken()); + public String getAuthorizationBaseUrl() { + return AUTHORIZE_URL; } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/Asana20Api.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/Asana20Api.java new file mode 100644 index 000000000..5e769aab8 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/Asana20Api.java @@ -0,0 +1,27 @@ +package com.github.scribejava.apis; + +import com.github.scribejava.core.builder.api.DefaultApi20; + +public class Asana20Api extends DefaultApi20 { + + protected Asana20Api() { + } + + private static class InstanceHolder { + private static final Asana20Api INSTANCE = new Asana20Api(); + } + + public static Asana20Api instance() { + return InstanceHolder.INSTANCE; + } + + @Override + public String getAccessTokenEndpoint() { + return "https://app.asana.com/-/oauth_token"; + } + + @Override + protected String getAuthorizationBaseUrl() { + return "https://app.asana.com/-/oauth_authorize"; + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/AutomaticAPI.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/AutomaticAPI.java new file mode 100644 index 000000000..58c82e040 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/AutomaticAPI.java @@ -0,0 +1,44 @@ +package com.github.scribejava.apis; + +import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.oauth2.clientauthentication.ClientAuthentication; +import com.github.scribejava.core.oauth2.clientauthentication.RequestBodyAuthenticationScheme; + +public class AutomaticAPI extends DefaultApi20 { + + private static final String AUTHORIZE_URL = "https://accounts.automatic.com/oauth/authorize"; + private static final String REFRESH_TOKEN_ENDPOINT = "https://accounts.automatic.com/oauth/refresh_token"; + private static final String ACCESS_TOKEN_ENDPOINT = "https://accounts.automatic.com/oauth/access_token"; + + protected AutomaticAPI() { + } + + private static class InstanceHolder { + + private static final AutomaticAPI INSTANCE = new AutomaticAPI(); + } + + public static AutomaticAPI instance() { + return InstanceHolder.INSTANCE; + } + + @Override + public String getAccessTokenEndpoint() { + return ACCESS_TOKEN_ENDPOINT; + } + + @Override + public String getRefreshTokenEndpoint() { + return REFRESH_TOKEN_ENDPOINT; + } + + @Override + protected String getAuthorizationBaseUrl() { + return AUTHORIZE_URL; + } + + @Override + public ClientAuthentication getClientAuthentication() { + return RequestBodyAuthenticationScheme.instance(); + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/BoxApi20.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/BoxApi20.java index d21cc39be..91a4b7c16 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/BoxApi20.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/BoxApi20.java @@ -1,7 +1,8 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.builder.api.OAuth2SignatureType; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignature; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignatureURIQueryParameter; /** * Box.com Api @@ -31,7 +32,7 @@ protected String getAuthorizationBaseUrl() { } @Override - public OAuth2SignatureType getSignatureType() { - return OAuth2SignatureType.BEARER_URI_QUERY_PARAMETER; + public BearerSignature getBearerSignature() { + return BearerSignatureURIQueryParameter.instance(); } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/DataportenApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/DataportenApi.java new file mode 100644 index 000000000..5e7ad9ceb --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/DataportenApi.java @@ -0,0 +1,27 @@ +package com.github.scribejava.apis; + +import com.github.scribejava.core.builder.api.DefaultApi20; + +public class DataportenApi extends DefaultApi20 { + + protected DataportenApi() { + } + + private static class InstanceHolder { + private static final DataportenApi INSTANCE = new DataportenApi(); + } + + public static DataportenApi instance() { + return InstanceHolder.INSTANCE; + } + + @Override + public String getAccessTokenEndpoint() { + return "https://auth.dataporten.no/oauth/token"; + } + + @Override + protected String getAuthorizationBaseUrl() { + return "https://auth.dataporten.no/oauth/authorization"; + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/DiggApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/DiggApi.java index 047537d8a..c7e8e1bd1 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/DiggApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/DiggApi.java @@ -1,11 +1,10 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi10a; -import com.github.scribejava.core.model.OAuth1RequestToken; public class DiggApi extends DefaultApi10a { - private static final String AUTHORIZATION_URL = "http://digg.com/oauth/authorize?oauth_token=%s"; + private static final String AUTHORIZATION_URL = "http://digg.com/oauth/authorize"; private static final String BASE_URL = "http://services.digg.com/oauth/"; protected DiggApi() { @@ -30,8 +29,7 @@ public String getAccessTokenEndpoint() { } @Override - public String getAuthorizationUrl(OAuth1RequestToken requestToken) { - return String.format(AUTHORIZATION_URL, requestToken.getToken()); + public String getAuthorizationBaseUrl() { + return AUTHORIZATION_URL; } - } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/DiscordApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/DiscordApi.java new file mode 100644 index 000000000..0583b6dc5 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/DiscordApi.java @@ -0,0 +1,32 @@ +package com.github.scribejava.apis; + +import com.github.scribejava.core.builder.api.DefaultApi20; + +public class DiscordApi extends DefaultApi20 { + + private DiscordApi() { + } + + private static class InstanceHolder { + private static final DiscordApi INSTANCE = new DiscordApi(); + } + + public static DiscordApi instance() { + return DiscordApi.InstanceHolder.INSTANCE; + } + + @Override + protected String getAuthorizationBaseUrl() { + return "https://discordapp.com/api/oauth2/authorize"; + } + + @Override + public String getRevokeTokenEndpoint() { + return "https://discordapp.com/api/oauth2/token/revoke"; + } + + @Override + public String getAccessTokenEndpoint() { + return "https://discordapp.com/api/oauth2/token"; + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/DropboxApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/DropboxApi.java new file mode 100644 index 000000000..8bbd646f5 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/DropboxApi.java @@ -0,0 +1,31 @@ +package com.github.scribejava.apis; + +import com.github.scribejava.core.builder.api.DefaultApi20; + +/** + * Dropbox.com Api + */ +public class DropboxApi extends DefaultApi20 { + + protected DropboxApi() { + } + + private static class InstanceHolder { + + private static final DropboxApi INSTANCE = new DropboxApi(); + } + + public static DropboxApi instance() { + return InstanceHolder.INSTANCE; + } + + @Override + public String getAccessTokenEndpoint() { + return "https://api.dropbox.com/oauth2/token"; + } + + @Override + protected String getAuthorizationBaseUrl() { + return "https://www.dropbox.com/oauth2/authorize"; + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/EtsyApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/EtsyApi.java new file mode 100644 index 000000000..00ca72918 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/EtsyApi.java @@ -0,0 +1,52 @@ +package com.github.scribejava.apis; + +import com.github.scribejava.core.builder.api.DefaultApi10a; + +public class EtsyApi extends DefaultApi10a { + + private static final String AUTHORIZE_URL = "https://www.etsy.com/oauth/signin"; + private static final String ACCESS_TOKEN_URL = "https://openapi.etsy.com/v2/oauth/access_token"; + private static final String REQUEST_TOKEN_URL = "https://openapi.etsy.com/v2/oauth/request_token"; + + private final String scopeAsString; + + private EtsyApi() { + scopeAsString = null; + } + + private EtsyApi(String... scopes) { + final StringBuilder builder = new StringBuilder(); + for (String scope : scopes) { + builder.append("%20").append(scope); + } + scopeAsString = "?scope=" + builder.substring(3); + } + + private static class InstanceHolder { + + private static final EtsyApi INSTANCE = new EtsyApi(); + } + + public static EtsyApi instance() { + return InstanceHolder.INSTANCE; + } + + public static EtsyApi instance(String... scopes) { + return scopes == null || scopes.length == 0 ? instance() : new EtsyApi(scopes); + } + + @Override + public String getAccessTokenEndpoint() { + return ACCESS_TOKEN_URL; + } + + @Override + public String getRequestTokenEndpoint() { + return scopeAsString == null ? REQUEST_TOKEN_URL : REQUEST_TOKEN_URL + scopeAsString; + } + + @Override + public String getAuthorizationBaseUrl() { + return AUTHORIZE_URL; + } +} 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 f761aab03..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 @@ -1,17 +1,28 @@ package com.github.scribejava.apis; +import java.io.OutputStream; + import com.github.scribejava.apis.facebook.FacebookAccessTokenJsonExtractor; +import com.github.scribejava.apis.facebook.FacebookService; 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.model.Verb; +import com.github.scribejava.core.oauth2.clientauthentication.ClientAuthentication; +import com.github.scribejava.core.oauth2.clientauthentication.RequestBodyAuthenticationScheme; -/** - * Facebook v2.8 API - */ public class FacebookApi extends DefaultApi20 { + private final String version; + protected FacebookApi() { + this("3.2"); + } + + protected FacebookApi(String version) { + this.version = version; } private static class InstanceHolder { @@ -23,6 +34,10 @@ public static FacebookApi instance() { return InstanceHolder.INSTANCE; } + public static FacebookApi customVersion(String version) { + return new FacebookApi(version); + } + @Override public Verb getAccessTokenVerb() { return Verb.GET; @@ -30,21 +45,34 @@ public Verb getAccessTokenVerb() { @Override public String getAccessTokenEndpoint() { - return "https://graph.facebook.com/v2.8/oauth/access_token"; + return "https://graph.facebook.com/v" + version + "/oauth/access_token"; } @Override public String getRefreshTokenEndpoint() { - throw new UnsupportedOperationException("Facebook doesn't support refershing tokens"); + throw new UnsupportedOperationException("Facebook doesn't support refreshing tokens"); } @Override protected String getAuthorizationBaseUrl() { - return "https://www.facebook.com/v2.8/dialog/oauth"; + return "https://www.facebook.com/v" + version + "/dialog/oauth"; } @Override public TokenExtractor getAccessTokenExtractor() { return FacebookAccessTokenJsonExtractor.instance(); } + + @Override + public ClientAuthentication getClientAuthentication() { + return RequestBodyAuthenticationScheme.instance(); + } + + @Override + public FacebookService createService(String apiKey, String apiSecret, String callback, String defaultScope, + String responseType, OutputStream debugStream, String userAgent, HttpClientConfig httpClientConfig, + HttpClient httpClient) { + return new FacebookService(this, apiKey, apiSecret, callback, defaultScope, responseType, debugStream, + userAgent, httpClientConfig, httpClient); + } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/FitbitApi20.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/FitbitApi20.java new file mode 100644 index 000000000..f6e163978 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/FitbitApi20.java @@ -0,0 +1,38 @@ +package com.github.scribejava.apis; + +import com.github.scribejava.apis.fitbit.FitBitJsonTokenExtractor; +import com.github.scribejava.core.builder.api.DefaultApi20; + + +/** + * Fitbit's OAuth2 client's implementation + * source: https://dev.fitbit.com/docs/oauth2/ + */ +public class FitbitApi20 extends DefaultApi20 { + + protected FitbitApi20() { + } + + private static class InstanceHolder { + private static final FitbitApi20 INSTANCE = new FitbitApi20(); + } + + public static FitbitApi20 instance() { + return InstanceHolder.INSTANCE; + } + + @Override + public String getAccessTokenEndpoint() { + return "https://api.fitbit.com/oauth2/token"; + } + + @Override + protected String getAuthorizationBaseUrl() { + return "https://www.fitbit.com/oauth2/authorize"; + } + + @Override + public FitBitJsonTokenExtractor getAccessTokenExtractor() { + return FitBitJsonTokenExtractor.instance(); + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/FlickrApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/FlickrApi.java index aafc6bbd8..eedb89890 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/FlickrApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/FlickrApi.java @@ -1,7 +1,6 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi10a; -import com.github.scribejava.core.model.OAuth1RequestToken; /** * OAuth API for Flickr. @@ -10,7 +9,7 @@ */ public class FlickrApi extends DefaultApi10a { - private static final String AUTHORIZE_URL = "https://www.flickr.com/services/oauth/authorize?oauth_token=%s"; + private static final String AUTHORIZE_URL = "https://www.flickr.com/services/oauth/authorize"; public enum FlickrPerm { READ, WRITE, DELETE @@ -30,6 +29,7 @@ protected FlickrApi(FlickrPerm perm) { } private static class InstanceHolder { + private static final FlickrApi INSTANCE = new FlickrApi(); } @@ -49,13 +49,9 @@ public String getAccessTokenEndpoint() { return "https://www.flickr.com/services/oauth/access_token"; } - /** - * {@inheritDoc} - */ @Override - public String getAuthorizationUrl(OAuth1RequestToken requestToken) { - final String authUrl = String.format(AUTHORIZE_URL, requestToken.getToken()); - return permString == null ? authUrl : authUrl + "&perms=" + permString; + public String getAuthorizationBaseUrl() { + return permString == null ? AUTHORIZE_URL : AUTHORIZE_URL + "?perms=" + permString; } /** diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/Foursquare2Api.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/Foursquare2Api.java index 321554af2..a90547f3a 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/Foursquare2Api.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/Foursquare2Api.java @@ -1,8 +1,9 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.builder.api.OAuth2SignatureType; import com.github.scribejava.core.model.Verb; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignature; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignatureURIQueryParameter; public class Foursquare2Api extends DefaultApi20 { @@ -33,7 +34,7 @@ protected String getAuthorizationBaseUrl() { } @Override - public OAuth2SignatureType getSignatureType() { - return OAuth2SignatureType.BEARER_URI_QUERY_PARAMETER; + public BearerSignature getBearerSignature() { + return BearerSignatureURIQueryParameter.instance(); } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/FoursquareApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/FoursquareApi.java index 69111d235..d10a184db 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/FoursquareApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/FoursquareApi.java @@ -1,11 +1,10 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi10a; -import com.github.scribejava.core.model.OAuth1RequestToken; public class FoursquareApi extends DefaultApi10a { - private static final String AUTHORIZATION_URL = "http://foursquare.com/oauth/authorize?oauth_token=%s"; + private static final String AUTHORIZATION_URL = "http://foursquare.com/oauth/authorize"; protected FoursquareApi() { } @@ -29,7 +28,7 @@ public String getRequestTokenEndpoint() { } @Override - public String getAuthorizationUrl(OAuth1RequestToken requestToken) { - return String.format(AUTHORIZATION_URL, requestToken.getToken()); + public String getAuthorizationBaseUrl() { + return AUTHORIZATION_URL; } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/FreelancerApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/FreelancerApi.java index 65b64c04f..33844d193 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/FreelancerApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/FreelancerApi.java @@ -2,12 +2,11 @@ import com.github.scribejava.core.builder.api.DefaultApi10a; import com.github.scribejava.core.builder.api.OAuth1SignatureType; -import com.github.scribejava.core.model.OAuth1RequestToken; import com.github.scribejava.core.model.Verb; public class FreelancerApi extends DefaultApi10a { - private static final String AUTHORIZATION_URL = "http://www.freelancer.com/users/api-token/auth.php?oauth_token=%s"; + private static final String AUTHORIZATION_URL = "http://www.freelancer.com/users/api-token/auth.php"; protected FreelancerApi() { } @@ -22,7 +21,7 @@ public static FreelancerApi instance() { @Override public OAuth1SignatureType getSignatureType() { - return OAuth1SignatureType.QueryString; + return OAuth1SignatureType.QUERY_STRING; } @Override @@ -46,8 +45,8 @@ public Verb getRequestTokenVerb() { } @Override - public String getAuthorizationUrl(OAuth1RequestToken requestToken) { - return String.format(AUTHORIZATION_URL, requestToken.getToken()); + public String getAuthorizationBaseUrl() { + return AUTHORIZATION_URL; } public static class Sandbox extends FreelancerApi { @@ -77,8 +76,8 @@ public String getAccessTokenEndpoint() { } @Override - public String getAuthorizationUrl(OAuth1RequestToken requestToken) { - return String.format(SANDBOX_AUTHORIZATION_URL + "?oauth_token=%s", requestToken.getToken()); + public String getAuthorizationBaseUrl() { + return SANDBOX_AUTHORIZATION_URL; } } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/GoogleApi20.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/GoogleApi20.java index 1e4bd8c6f..e3de4c746 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/GoogleApi20.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/GoogleApi20.java @@ -1,7 +1,9 @@ package com.github.scribejava.apis; +import com.github.scribejava.apis.google.GoogleDeviceAuthorizationJsonExtractor; import com.github.scribejava.apis.openid.OpenIdJsonTokenExtractor; import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.extractors.DeviceAuthorizationJsonExtractor; import com.github.scribejava.core.extractors.TokenExtractor; import com.github.scribejava.core.model.OAuth2AccessToken; @@ -11,6 +13,7 @@ protected GoogleApi20() { } private static class InstanceHolder { + private static final GoogleApi20 INSTANCE = new GoogleApi20(); } @@ -20,16 +23,31 @@ public static GoogleApi20 instance() { @Override public String getAccessTokenEndpoint() { - return "https://www.googleapis.com/oauth2/v4/token"; + return "https://oauth2.googleapis.com/token"; } @Override protected String getAuthorizationBaseUrl() { - return "https://accounts.google.com/o/oauth2/auth"; + return "https://accounts.google.com/o/oauth2/v2/auth"; } @Override public TokenExtractor getAccessTokenExtractor() { return OpenIdJsonTokenExtractor.instance(); } + + @Override + public String getRevokeTokenEndpoint() { + return "https://oauth2.googleapis.com/revoke"; + } + + @Override + public String getDeviceAuthorizationEndpoint() { + return "https://oauth2.googleapis.com/device/code"; + } + + @Override + public DeviceAuthorizationJsonExtractor getDeviceAuthorizationExtractor() { + return GoogleDeviceAuthorizationJsonExtractor.instance(); + } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/HHApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/HHApi.java index 2a8a8aa12..61476eaef 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/HHApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/HHApi.java @@ -1,6 +1,8 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.oauth2.clientauthentication.ClientAuthentication; +import com.github.scribejava.core.oauth2.clientauthentication.RequestBodyAuthenticationScheme; public class HHApi extends DefaultApi20 { @@ -8,6 +10,7 @@ protected HHApi() { } private static class InstanceHolder { + private static final HHApi INSTANCE = new HHApi(); } @@ -24,4 +27,9 @@ public String getAccessTokenEndpoint() { protected String getAuthorizationBaseUrl() { return "https://hh.ru/oauth/authorize"; } + + @Override + public ClientAuthentication getClientAuthentication() { + return RequestBodyAuthenticationScheme.instance(); + } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/HiOrgServerApi20.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/HiOrgServerApi20.java new file mode 100644 index 000000000..a69673a25 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/HiOrgServerApi20.java @@ -0,0 +1,44 @@ +package com.github.scribejava.apis; + +import com.github.scribejava.core.builder.api.DefaultApi20; + +/** + * OAuth2 API for HiOrg-Server + * + * @see HiOrg-Server OAuth API documentation + */ +public class HiOrgServerApi20 extends DefaultApi20 { + + private final String version; + + protected HiOrgServerApi20() { + this("v1"); + } + + protected HiOrgServerApi20(String version) { + this.version = version; + } + + private static class InstanceHolder { + + private static final HiOrgServerApi20 INSTANCE = new HiOrgServerApi20(); + } + + public static HiOrgServerApi20 instance() { + return InstanceHolder.INSTANCE; + } + + public static HiOrgServerApi20 customVersion(String version) { + return new HiOrgServerApi20(version); + } + + @Override + public String getAccessTokenEndpoint() { + return "https://www.hiorg-server.de/api/oauth2/" + version + "/token.php"; + } + + @Override + protected String getAuthorizationBaseUrl() { + return "https://www.hiorg-server.de/api/oauth2/" + version + "/authorize.php"; + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/ImgurApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/ImgurApi.java index 3b14473d1..845fd95cb 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/ImgurApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/ImgurApi.java @@ -1,11 +1,13 @@ package com.github.scribejava.apis; -import com.github.scribejava.apis.service.ImgurOAuthServiceImpl; +import com.github.scribejava.apis.imgur.ImgurOAuthService; import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.model.OAuthConfig; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; import com.github.scribejava.core.model.OAuthConstants; import com.github.scribejava.core.model.ParameterList; -import com.github.scribejava.core.oauth.OAuth20Service; + +import java.io.OutputStream; import java.util.Map; public class ImgurApi extends DefaultApi20 { @@ -14,6 +16,7 @@ protected ImgurApi() { } private static class InstanceHolder { + private static final ImgurApi INSTANCE = new ImgurApi(); } @@ -27,22 +30,20 @@ public String getAccessTokenEndpoint() { } @Override - public String getAuthorizationUrl(OAuthConfig config, Map additionalParams) { + public String getAuthorizationUrl(String responseType, String apiKey, String callback, String scope, String state, + Map additionalParams) { final ParameterList parameters = new ParameterList(additionalParams); - parameters.add(OAuthConstants.RESPONSE_TYPE, isOob(config) ? "pin" : "code"); - parameters.add(OAuthConstants.CLIENT_ID, config.getApiKey()); + parameters.add(OAuthConstants.RESPONSE_TYPE, isOob(callback) ? "pin" : "code"); + parameters.add(OAuthConstants.CLIENT_ID, apiKey); - final String callback = config.getCallback(); if (callback != null) { parameters.add(OAuthConstants.REDIRECT_URI, callback); } - final String scope = config.getScope(); if (scope != null) { parameters.add(OAuthConstants.SCOPE, scope); } - final String state = config.getState(); if (state != null) { parameters.add(OAuthConstants.STATE, state); } @@ -56,11 +57,14 @@ protected String getAuthorizationBaseUrl() { } @Override - public OAuth20Service createService(OAuthConfig config) { - return new ImgurOAuthServiceImpl(this, config); + public ImgurOAuthService createService(String apiKey, String apiSecret, String callback, String defaultScope, + String responseType, OutputStream debugStream, String userAgent, HttpClientConfig httpClientConfig, + HttpClient httpClient) { + return new ImgurOAuthService(this, apiKey, apiSecret, callback, defaultScope, responseType, debugStream, + userAgent, httpClientConfig, httpClient); } - public static boolean isOob(OAuthConfig config) { - return "oob".equals(config.getCallback()); + public static boolean isOob(String callback) { + return OAuthConstants.OOB.equals(callback); } } 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/KaixinApi20.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/KaixinApi20.java index 927813535..b94e8e1a3 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/KaixinApi20.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/KaixinApi20.java @@ -1,8 +1,9 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.builder.api.OAuth2SignatureType; import com.github.scribejava.core.model.Verb; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignature; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignatureURIQueryParameter; /** * Kaixin(http://www.kaixin001.com/) open platform api based on OAuth 2.0. @@ -36,7 +37,7 @@ protected String getAuthorizationBaseUrl() { } @Override - public OAuth2SignatureType getSignatureType() { - return OAuth2SignatureType.BEARER_URI_QUERY_PARAMETER; + public BearerSignature getBearerSignature() { + return BearerSignatureURIQueryParameter.instance(); } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/KakaoApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/KakaoApi.java new file mode 100644 index 000000000..6587fe32f --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/KakaoApi.java @@ -0,0 +1,35 @@ +package com.github.scribejava.apis; + +import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.oauth2.clientauthentication.ClientAuthentication; +import com.github.scribejava.core.oauth2.clientauthentication.RequestBodyAuthenticationScheme; + +public class KakaoApi extends DefaultApi20 { + + protected KakaoApi() { + } + + private static class InstanceHolder { + + private static final KakaoApi INSTANCE = new KakaoApi(); + } + + public static KakaoApi instance() { + return KakaoApi.InstanceHolder.INSTANCE; + } + + @Override + public String getAccessTokenEndpoint() { + return "https://kauth.kakao.com/oauth/token"; + } + + @Override + protected String getAuthorizationBaseUrl() { + return "https://kauth.kakao.com/oauth/authorize"; + } + + @Override + public ClientAuthentication getClientAuthentication() { + return RequestBodyAuthenticationScheme.instance(); + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/KeycloakApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/KeycloakApi.java new file mode 100644 index 000000000..ed713b4d0 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/KeycloakApi.java @@ -0,0 +1,63 @@ +package com.github.scribejava.apis; + +import com.github.scribejava.apis.openid.OpenIdJsonTokenExtractor; +import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.extractors.TokenExtractor; +import com.github.scribejava.core.model.OAuth2AccessToken; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +public class KeycloakApi extends DefaultApi20 { + + private static final ConcurrentMap INSTANCES = new ConcurrentHashMap<>(); + + private final String baseUrlWithRealm; + + protected KeycloakApi(String baseUrlWithRealm) { + this.baseUrlWithRealm = baseUrlWithRealm; + } + + public static KeycloakApi instance() { + return instance("http://localhost:8080/", "master"); + } + + public static KeycloakApi instance(String baseUrl, String realm) { + final String defaultBaseUrlWithRealm = composeBaseUrlWithRealm(baseUrl, realm); + + //java8: switch to ConcurrentMap::computeIfAbsent + KeycloakApi api = INSTANCES.get(defaultBaseUrlWithRealm); + if (api == null) { + api = new KeycloakApi(defaultBaseUrlWithRealm); + final KeycloakApi alreadyCreatedApi = INSTANCES.putIfAbsent(defaultBaseUrlWithRealm, api); + if (alreadyCreatedApi != null) { + return alreadyCreatedApi; + } + } + return api; + } + + protected static String composeBaseUrlWithRealm(String baseUrl, String realm) { + return baseUrl + (baseUrl.endsWith("/") ? "" : "/") + "auth/realms/" + realm; + } + + @Override + public String getAccessTokenEndpoint() { + return baseUrlWithRealm + "/protocol/openid-connect/token"; + } + + @Override + protected String getAuthorizationBaseUrl() { + return baseUrlWithRealm + "/protocol/openid-connect/auth"; + } + + @Override + public TokenExtractor getAccessTokenExtractor() { + return OpenIdJsonTokenExtractor.instance(); + } + + @Override + public String getRevokeTokenEndpoint() { + throw new RuntimeException("Not implemented yet"); + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/LinkedInApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/LinkedInApi.java index f30b44d17..62258bbec 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/LinkedInApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/LinkedInApi.java @@ -1,11 +1,10 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi10a; -import com.github.scribejava.core.model.OAuth1RequestToken; public class LinkedInApi extends DefaultApi10a { - private static final String AUTHORIZE_URL = "https://api.linkedin.com/uas/oauth/authenticate?oauth_token=%s"; + private static final String AUTHORIZE_URL = "https://api.linkedin.com/uas/oauth/authenticate"; private static final String REQUEST_TOKEN_URL = "https://api.linkedin.com/uas/oauth/requestToken"; private final String scopesAsString; @@ -46,7 +45,7 @@ public String getRequestTokenEndpoint() { } @Override - public String getAuthorizationUrl(OAuth1RequestToken requestToken) { - return String.format(AUTHORIZE_URL, requestToken.getToken()); + public String getAuthorizationBaseUrl() { + return AUTHORIZE_URL; } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/LinkedInApi20.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/LinkedInApi20.java index 0d22d705c..36d4ab702 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/LinkedInApi20.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/LinkedInApi20.java @@ -1,6 +1,8 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.oauth2.clientauthentication.ClientAuthentication; +import com.github.scribejava.core.oauth2.clientauthentication.RequestBodyAuthenticationScheme; public class LinkedInApi20 extends DefaultApi20 { @@ -24,4 +26,9 @@ public String getAccessTokenEndpoint() { protected String getAuthorizationBaseUrl() { return "https://www.linkedin.com/oauth/v2/authorization"; } + + @Override + public ClientAuthentication getClientAuthentication() { + return RequestBodyAuthenticationScheme.instance(); + } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/LiveApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/LiveApi.java index 83cd89780..7a0a64ea5 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/LiveApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/LiveApi.java @@ -1,7 +1,8 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.builder.api.OAuth2SignatureType; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignature; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignatureURIQueryParameter; public class LiveApi extends DefaultApi20 { @@ -27,7 +28,7 @@ protected String getAuthorizationBaseUrl() { } @Override - public OAuth2SignatureType getSignatureType() { - return OAuth2SignatureType.BEARER_URI_QUERY_PARAMETER; + public BearerSignature getBearerSignature() { + return BearerSignatureURIQueryParameter.instance(); } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/MailruApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/MailruApi.java index a67e434c5..b48091594 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/MailruApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/MailruApi.java @@ -1,9 +1,11 @@ package com.github.scribejava.apis; +import java.io.OutputStream; + +import com.github.scribejava.apis.mailru.MailruOAuthService; import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.model.OAuthConfig; -import com.github.scribejava.apis.service.MailruOAuthServiceImpl; -import com.github.scribejava.core.oauth.OAuth20Service; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; public class MailruApi extends DefaultApi20 { @@ -11,6 +13,7 @@ protected MailruApi() { } private static class InstanceHolder { + private static final MailruApi INSTANCE = new MailruApi(); } @@ -29,7 +32,10 @@ protected String getAuthorizationBaseUrl() { } @Override - public OAuth20Service createService(OAuthConfig config) { - return new MailruOAuthServiceImpl(this, config); + public MailruOAuthService createService(String apiKey, String apiSecret, String callback, String defaultScope, + String responseType, OutputStream debugStream, String userAgent, HttpClientConfig httpClientConfig, + HttpClient httpClient) { + return new MailruOAuthService(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 new file mode 100644 index 000000000..3bb18001d --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/MediaWikiApi.java @@ -0,0 +1,77 @@ +package com.github.scribejava.apis; + +import com.github.scribejava.core.builder.api.DefaultApi10a; + +public class MediaWikiApi extends DefaultApi10a { + + private static class InstanceHolder { + + private static final MediaWikiApi INSTANCE = new MediaWikiApi( + "https://meta.wikimedia.org/w/index.php", + "https://meta.wikimedia.org/wiki/" + ); + } + + private static class BetaInstanceHolder { + + private static final MediaWikiApi BETA_INSTANCE = new MediaWikiApi( + "https://meta.wikimedia.beta.wmflabs.org/w/index.php", + "https://meta.wikimedia.beta.wmflabs.org/wiki/" + ); + } + + private final String indexUrl; + private final String niceUrlBase; + + /** + * @param indexUrl The URL to the index.php of the wiki. Due to a + * MediaWiki bug, some requests must currently use the non-nice URL. + * @param niceUrlBase The base of nice URLs for the wiki, including the trailing slash. Due to + * another MediaWiki bug, some requests must currently use + * the nice URL. + */ + public MediaWikiApi(String indexUrl, String niceUrlBase) { + this.indexUrl = indexUrl; + this.niceUrlBase = niceUrlBase; + } + + /** + * The instance for wikis hosted by the Wikimedia Foundation. Consumers are requested on + * + * Special:OAuthConsumerRegistration/propose + * . + * + * @return instance + */ + public static MediaWikiApi instance() { + return InstanceHolder.INSTANCE; + } + + /** + * The instance for wikis in the Wikimedia Foundation’s Beta Cluster. Consumers are requested on + * + * Special:OAuthConsumerRegistration/propose + * . + * + * @return instanceBeta + */ + public static MediaWikiApi instanceBeta() { + return BetaInstanceHolder.BETA_INSTANCE; + } + + @Override + public String getRequestTokenEndpoint() { + return indexUrl + "?title=Special:OAuth/initiate"; + } + + @Override + public String getAuthorizationBaseUrl() { + return niceUrlBase + "Special:OAuth/authorize"; + } + + @Override + public String getAccessTokenEndpoint() { + return indexUrl + "?title=Special:OAuth/token"; + } + +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/MeetupApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/MeetupApi.java index b00db6159..b2acc5680 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/MeetupApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/MeetupApi.java @@ -1,14 +1,13 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi10a; -import com.github.scribejava.core.model.OAuth1RequestToken; /** * OAuth access to the Meetup.com API. For more information visit http://www.meetup.com/api */ public class MeetupApi extends DefaultApi10a { - private static final String AUTHORIZE_URL = "http://www.meetup.com/authenticate?oauth_token=%s"; + private static final String AUTHORIZE_URL = "http://www.meetup.com/authenticate"; protected MeetupApi() { } @@ -32,7 +31,7 @@ public String getAccessTokenEndpoint() { } @Override - public String getAuthorizationUrl(OAuth1RequestToken requestToken) { - return String.format(AUTHORIZE_URL, requestToken.getToken()); + public String getAuthorizationBaseUrl() { + return AUTHORIZE_URL; } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/MeetupApi20.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/MeetupApi20.java new file mode 100644 index 000000000..ce09e931d --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/MeetupApi20.java @@ -0,0 +1,34 @@ +package com.github.scribejava.apis; + +import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.oauth2.clientauthentication.ClientAuthentication; +import com.github.scribejava.core.oauth2.clientauthentication.RequestBodyAuthenticationScheme; + +public class MeetupApi20 extends DefaultApi20 { + + protected MeetupApi20() { + } + + private static class InstanceHolder { + private static final MeetupApi20 INSTANCE = new MeetupApi20(); + } + + public static MeetupApi20 instance() { + return InstanceHolder.INSTANCE; + } + + @Override + public String getAccessTokenEndpoint() { + return "https://secure.meetup.com/oauth2/access"; + } + + @Override + protected String getAuthorizationBaseUrl() { + return "https://secure.meetup.com/oauth2/authorize"; + } + + @Override + public ClientAuthentication getClientAuthentication() { + return RequestBodyAuthenticationScheme.instance(); + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/MicrosoftAzureActiveDirectory20Api.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/MicrosoftAzureActiveDirectory20Api.java new file mode 100644 index 000000000..399cf54c9 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/MicrosoftAzureActiveDirectory20Api.java @@ -0,0 +1,48 @@ +package com.github.scribejava.apis; + +import com.github.scribejava.apis.microsoftazureactivedirectory.BaseMicrosoftAzureActiveDirectoryApi; +import com.github.scribejava.apis.microsoftazureactivedirectory.MicrosoftAzureActiveDirectory20BearerSignature; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignature; + +/** + * Microsoft Azure Active Directory Api v 2.0 + * + * @see + * Understand the OAuth 2.0 authorization code flow in Azure AD | Microsoft Docs + * @see + * Microsoft Graph REST API v1.0 reference + * @see https://portal.azure.com + */ +public class MicrosoftAzureActiveDirectory20Api extends BaseMicrosoftAzureActiveDirectoryApi { + + protected MicrosoftAzureActiveDirectory20Api() { + this(COMMON_TENANT); + } + + protected MicrosoftAzureActiveDirectory20Api(String tenant) { + super(tenant); + } + + private static class InstanceHolder { + + private static final MicrosoftAzureActiveDirectory20Api INSTANCE = new MicrosoftAzureActiveDirectory20Api(); + } + + public static MicrosoftAzureActiveDirectory20Api instance() { + return InstanceHolder.INSTANCE; + } + + public static MicrosoftAzureActiveDirectory20Api custom(String tenant) { + return new MicrosoftAzureActiveDirectory20Api(tenant); + } + + @Override + public BearerSignature getBearerSignature() { + return MicrosoftAzureActiveDirectory20BearerSignature.instance(); + } + + @Override + protected String getEndpointVersionPath() { + return "/v2.0"; + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/MicrosoftAzureActiveDirectoryApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/MicrosoftAzureActiveDirectoryApi.java new file mode 100644 index 000000000..9ab4aa291 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/MicrosoftAzureActiveDirectoryApi.java @@ -0,0 +1,64 @@ +package com.github.scribejava.apis; + +import com.github.scribejava.apis.microsoftazureactivedirectory.BaseMicrosoftAzureActiveDirectoryApi; +import com.github.scribejava.apis.microsoftazureactivedirectory.MicrosoftAzureActiveDirectoryBearerSignature; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignature; + +/** + * Microsoft Azure Active Directory Api + * + * @see + * Understand the OAuth 2.0 authorization code flow in Azure AD | Microsoft Docs + * @see + * Azure AD Java web app Getting Started | Microsoft Docs + * @see + * Azure AD Graph API Operations on the Signed-in User + * @see https://portal.azure.com + */ +public class MicrosoftAzureActiveDirectoryApi extends BaseMicrosoftAzureActiveDirectoryApi { + + private final String resource; + + protected MicrosoftAzureActiveDirectoryApi() { + this(COMMON_TENANT, null); + } + + protected MicrosoftAzureActiveDirectoryApi(String tenant, String resource) { + super(tenant); + this.resource = resource; + } + + private static class InstanceHolder { + + private static final MicrosoftAzureActiveDirectoryApi INSTANCE = new MicrosoftAzureActiveDirectoryApi(); + } + + public static MicrosoftAzureActiveDirectoryApi instance() { + return InstanceHolder.INSTANCE; + } + + public static MicrosoftAzureActiveDirectoryApi customTenant(String tenant) { + return new MicrosoftAzureActiveDirectoryApi(tenant, null); + } + + public static MicrosoftAzureActiveDirectoryApi customResource(String resource) { + return new MicrosoftAzureActiveDirectoryApi(COMMON_TENANT, resource); + } + + public static MicrosoftAzureActiveDirectoryApi custom(String tenant, String resource) { + return new MicrosoftAzureActiveDirectoryApi(tenant, resource); + } + + @Override + protected String getAuthorizationBaseUrl() { + final String authorizationBaseUrl = super.getAuthorizationBaseUrl(); + return resource == null || resource.isEmpty() ? authorizationBaseUrl + : authorizationBaseUrl + "?resource=" + resource; + } + + @Override + public BearerSignature getBearerSignature() { + return MicrosoftAzureActiveDirectoryBearerSignature.instance(); + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/MisfitApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/MisfitApi.java index c34d2cc15..051de70c1 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/MisfitApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/MisfitApi.java @@ -1,7 +1,8 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.builder.api.OAuth2SignatureType; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignature; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignatureURIQueryParameter; public class MisfitApi extends DefaultApi20 { @@ -28,7 +29,7 @@ protected String getAuthorizationBaseUrl() { } @Override - public OAuth2SignatureType getSignatureType() { - return OAuth2SignatureType.BEARER_URI_QUERY_PARAMETER; + public BearerSignature getBearerSignature() { + return BearerSignatureURIQueryParameter.instance(); } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/NaverApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/NaverApi.java index a3028ae11..f60871735 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/NaverApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/NaverApi.java @@ -1,7 +1,8 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.builder.api.OAuth2SignatureType; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignature; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignatureURIQueryParameter; public class NaverApi extends DefaultApi20 { protected NaverApi() { @@ -9,9 +10,6 @@ protected NaverApi() { private static class InstanceHolder { private static final NaverApi INSTANCE = new NaverApi(); - - private InstanceHolder() { - } } public static NaverApi instance() { @@ -29,7 +27,7 @@ protected String getAuthorizationBaseUrl() { } @Override - public OAuth2SignatureType getSignatureType() { - return OAuth2SignatureType.BEARER_URI_QUERY_PARAMETER; + public BearerSignature getBearerSignature() { + return BearerSignatureURIQueryParameter.instance(); } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/NeteaseWeibooApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/NeteaseWeibooApi.java deleted file mode 100644 index 91edb38ca..000000000 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/NeteaseWeibooApi.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.github.scribejava.apis; - -import com.github.scribejava.core.builder.api.DefaultApi10a; -import com.github.scribejava.core.model.OAuth1RequestToken; -import com.github.scribejava.core.model.OAuth1Token; - -public class NeteaseWeibooApi extends DefaultApi10a { - - private static final String REQUEST_TOKEN_URL = "http://api.t.163.com/oauth/request_token"; - private static final String ACCESS_TOKEN_URL = "http://api.t.163.com/oauth/access_token"; - private static final String AUTHORIZE_URL = "http://api.t.163.com/oauth/authorize?oauth_token=%s"; - private static final String AUTHENTICATE_URL = "http://api.t.163.com/oauth/authenticate?oauth_token=%s"; - - protected NeteaseWeibooApi() { - } - - private static class InstanceHolder { - private static final NeteaseWeibooApi INSTANCE = new NeteaseWeibooApi(); - } - - public static NeteaseWeibooApi instance() { - return InstanceHolder.INSTANCE; - } - - @Override - public String getRequestTokenEndpoint() { - return REQUEST_TOKEN_URL; - } - - @Override - public String getAccessTokenEndpoint() { - return ACCESS_TOKEN_URL; - } - - /** - * this method will ignore your callback if you're creating a desktop client please choose this url else your can - * call getAuthenticateUrl - * - * via - * http://open.t.163.com/wiki/index.php?title=%E8%AF%B7%E6%B1%82%E7%94%A8%E6%88%B7%E6%8E%88%E6%9D%83Token(oauth/authorize) - * @return url to redirect user to (to get code) - */ - @Override - public String getAuthorizationUrl(OAuth1RequestToken requestToken) { - return String.format(AUTHORIZE_URL, requestToken.getToken()); - } - - /** - * this method is for web client with callback url if you're creating a desktop client please call - * getAuthorizationUrl - * - * via - * http://open.t.163.com/wiki/index.php?title=%E8%AF%B7%E6%B1%82%E7%94%A8%E6%88%B7%E6%8E%88%E6%9D%83Token(oauth/authenticate) - * - * @param requestToken Token - * @return String - */ - public String getAuthenticateUrl(OAuth1Token requestToken) { - return String.format(AUTHENTICATE_URL, requestToken.getToken()); - } -} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/OdnoklassnikiApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/OdnoklassnikiApi.java index f8e1a40ef..b87eaa072 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/OdnoklassnikiApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/OdnoklassnikiApi.java @@ -1,10 +1,15 @@ package com.github.scribejava.apis; -import com.github.scribejava.apis.service.OdnoklassnikiServiceImpl; +import java.io.OutputStream; + +import com.github.scribejava.apis.odnoklassniki.OdnoklassnikiOAuthService; import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.builder.api.OAuth2SignatureType; -import com.github.scribejava.core.model.OAuthConfig; -import com.github.scribejava.core.oauth.OAuth20Service; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignature; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignatureURIQueryParameter; +import com.github.scribejava.core.oauth2.clientauthentication.ClientAuthentication; +import com.github.scribejava.core.oauth2.clientauthentication.RequestBodyAuthenticationScheme; public class OdnoklassnikiApi extends DefaultApi20 { @@ -12,6 +17,7 @@ protected OdnoklassnikiApi() { } private static class InstanceHolder { + private static final OdnoklassnikiApi INSTANCE = new OdnoklassnikiApi(); } @@ -30,12 +36,20 @@ protected String getAuthorizationBaseUrl() { } @Override - public OAuth20Service createService(OAuthConfig config) { - return new OdnoklassnikiServiceImpl(this, config); + public OdnoklassnikiOAuthService createService(String apiKey, String apiSecret, String callback, + String defaultScope, String responseType, OutputStream debugStream, String userAgent, + HttpClientConfig httpClientConfig, HttpClient httpClient) { + return new OdnoklassnikiOAuthService(this, apiKey, apiSecret, callback, defaultScope, responseType, debugStream, + userAgent, httpClientConfig, httpClient); + } + + @Override + public BearerSignature getBearerSignature() { + return BearerSignatureURIQueryParameter.instance(); } @Override - public OAuth2SignatureType getSignatureType() { - return OAuth2SignatureType.BEARER_URI_QUERY_PARAMETER; + public ClientAuthentication getClientAuthentication() { + return RequestBodyAuthenticationScheme.instance(); } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/PinterestApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/PinterestApi.java index d93404b5e..70ef1caff 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/PinterestApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/PinterestApi.java @@ -1,7 +1,8 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.builder.api.OAuth2SignatureType; +import com.github.scribejava.core.oauth2.clientauthentication.ClientAuthentication; +import com.github.scribejava.core.oauth2.clientauthentication.RequestBodyAuthenticationScheme; public class PinterestApi extends DefaultApi20 { @@ -27,7 +28,7 @@ protected String getAuthorizationBaseUrl() { } @Override - public OAuth2SignatureType getSignatureType() { - return OAuth2SignatureType.BEARER_URI_QUERY_PARAMETER; + public ClientAuthentication getClientAuthentication() { + return RequestBodyAuthenticationScheme.instance(); } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/PolarAPI.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/PolarAPI.java new file mode 100644 index 000000000..01e1651c6 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/PolarAPI.java @@ -0,0 +1,53 @@ +package com.github.scribejava.apis; + +import com.github.scribejava.apis.polar.PolarJsonTokenExtractor; +import com.github.scribejava.apis.polar.PolarOAuthService; +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 java.io.OutputStream; + +/** + * Polar's OAuth2 client's implementation source: https://www.polar.com/accesslink-api/#authentication + */ +public class PolarAPI extends DefaultApi20 { + + protected PolarAPI() { + } + + private static class InstanceHolder { + + private static final PolarAPI INSTANCE = new PolarAPI(); + } + + public static PolarAPI instance() { + return PolarAPI.InstanceHolder.INSTANCE; + } + + @Override + public String getAccessTokenEndpoint() { + return "https://polarremote.com/v2/oauth2/token"; + } + + @Override + protected String getAuthorizationBaseUrl() { + return "https://flow.polar.com/oauth2/authorization"; + } + + @Override + public PolarOAuthService createService(String apiKey, String apiSecret, String callback, String defaultScope, + String responseType, OutputStream debugStream, String userAgent, HttpClientConfig httpClientConfig, + HttpClient httpClient) { + + return new PolarOAuthService(this, apiKey, apiSecret, callback, defaultScope, responseType, debugStream, + userAgent, httpClientConfig, httpClient); + } + + @Override + public TokenExtractor getAccessTokenExtractor() { + return PolarJsonTokenExtractor.instance(); + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/Px500Api.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/Px500Api.java index 2c4e6de3c..91e34a449 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/Px500Api.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/Px500Api.java @@ -1,11 +1,10 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi10a; -import com.github.scribejava.core.model.OAuth1RequestToken; public class Px500Api extends DefaultApi10a { - private static final String AUTHORIZATION_URL = "https://api.500px.com/v1/oauth/authorize?oauth_token=%s"; + private static final String AUTHORIZATION_URL = "https://api.500px.com/v1/oauth/authorize"; protected Px500Api() { } @@ -29,7 +28,7 @@ public String getRequestTokenEndpoint() { } @Override - public String getAuthorizationUrl(OAuth1RequestToken requestToken) { - return String.format(AUTHORIZATION_URL, requestToken.getToken()); + public String getAuthorizationBaseUrl() { + return AUTHORIZATION_URL; } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/RenrenApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/RenrenApi.java index cc475440e..d4ade9fad 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/RenrenApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/RenrenApi.java @@ -1,8 +1,9 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.builder.api.OAuth2SignatureType; import com.github.scribejava.core.model.Verb; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignature; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignatureURIQueryParameter; /** * Renren(http://www.renren.com/) OAuth 2.0 based api. @@ -36,7 +37,7 @@ protected String getAuthorizationBaseUrl() { } @Override - public OAuth2SignatureType getSignatureType() { - return OAuth2SignatureType.BEARER_URI_QUERY_PARAMETER; + public BearerSignature getBearerSignature() { + return BearerSignatureURIQueryParameter.instance(); } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/SalesforceApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/SalesforceApi.java index 95e5e2816..8760a7fff 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/SalesforceApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/SalesforceApi.java @@ -7,17 +7,19 @@ import com.github.scribejava.core.extractors.TokenExtractor; import com.github.scribejava.core.model.OAuth2AccessToken; import com.github.scribejava.core.model.Verb; +import com.github.scribejava.core.oauth2.clientauthentication.ClientAuthentication; +import com.github.scribejava.core.oauth2.clientauthentication.RequestBodyAuthenticationScheme; import java.io.IOException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; +import java.util.Arrays; import javax.net.ssl.SSLSocket; /** * This class is an implementation of the Salesforce OAuth2 API. - * The default implementation connects to the Salesforce - * production environment. - * If you want to connect to a Sandbox environment you've to use {@link #sandbox()} method to - * get sandbox instance of this API + * + * The default implementation connects to the Salesforce production environment. If you want to connect to a Sandbox + * environment you've to use {@link #sandbox()} method to get sandbox instance of this API */ public class SalesforceApi extends DefaultApi20 { @@ -49,6 +51,7 @@ protected SalesforceApi(String hostName) { } private static class InstanceHolder { + private static final SalesforceApi INSTANCE = new SalesforceApi(PRODUCTION_HOST); } @@ -82,7 +85,7 @@ public TokenExtractor getAccessTokenExtractor() { private static boolean isTLSv11orUpperEnabled(final SSLSocket socket) { for (String protocol : socket.getEnabledProtocols()) { - if ("TLSv1.2".equals(protocol) || "TLSv1.1".equals(protocol)) { + if (protocol.startsWith("TLSv1.")) { return true; } } @@ -95,7 +98,7 @@ private static boolean isTLSv11orUpperEnabled(final SSLSocket socket) { * Java 8 have TLS 1.2 enabled by default. java 7 - no, you should invoke this method or turn TLS>=1.1 somehow * else

* - * @throws java.security.NoSuchAlgorithmException in case your jvm doesn't support TLSv1.1 and TLSv1.2 + * @throws java.security.NoSuchAlgorithmException in case your jvm doesn't support TLSv1.1 or higher * @throws java.security.KeyManagementException unexpected Exception from * {@link SSLContext#init(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], java.security.SecureRandom)} * @throws java.io.IOException unexpected Exception from {@link javax.net.ssl.SSLSocketFactory#createSocket()} @@ -105,28 +108,23 @@ public static void initTLSv11orUpper() throws NoSuchAlgorithmException, KeyManag if (isTLSv11orUpperEnabled(socket)) { return; } - boolean supportTLSv11 = false; - boolean supportTLSv12 = false; - for (String protocol : socket.getSupportedProtocols()) { - if ("TLSv1.2".equals(protocol)) { - supportTLSv12 = true; - break; - } - if ("TLSv1.1".equals(protocol)) { - supportTLSv11 = true; + final String[] supportedProtocols = socket.getSupportedProtocols(); + Arrays.sort(supportedProtocols); + for (int i = supportedProtocols.length - 1; i >= 0; i--) { + final String supportedProtocol = supportedProtocols[i]; + if (supportedProtocol.startsWith("TLSv1.")) { + final SSLContext context = SSLContext.getInstance(supportedProtocol); + context.init(null, null, null); + SSLContext.setDefault(context); + return; } } - final SSLContext context; - if (supportTLSv12) { - context = SSLContext.getInstance("TLSv1.2"); - } else if (supportTLSv11) { - context = SSLContext.getInstance("TLSv1.1"); - } else { - throw new NoSuchAlgorithmException("for Salesforce API to work you need jvm with TLS 1.1 or 1.2 support"); - } + throw new NoSuchAlgorithmException("for Salesforce API to work you need jvm with TLS 1.1 or higher support"); + } - context.init(null, null, null); - SSLContext.setDefault(context); + @Override + public ClientAuthentication getClientAuthentication() { + return RequestBodyAuthenticationScheme.instance(); } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/SinaWeiboApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/SinaWeiboApi.java index b91108c18..9d69a9afc 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/SinaWeiboApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/SinaWeiboApi.java @@ -1,13 +1,12 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi10a; -import com.github.scribejava.core.model.OAuth1RequestToken; public class SinaWeiboApi extends DefaultApi10a { private static final String REQUEST_TOKEN_URL = "http://api.t.sina.com.cn/oauth/request_token"; private static final String ACCESS_TOKEN_URL = "http://api.t.sina.com.cn/oauth/access_token"; - private static final String AUTHORIZE_URL = "http://api.t.sina.com.cn/oauth/authorize?oauth_token=%s"; + private static final String AUTHORIZE_URL = "http://api.t.sina.com.cn/oauth/authorize"; protected SinaWeiboApi() { } @@ -31,7 +30,7 @@ public String getAccessTokenEndpoint() { } @Override - public String getAuthorizationUrl(OAuth1RequestToken requestToken) { - return String.format(AUTHORIZE_URL, requestToken.getToken()); + public String getAuthorizationBaseUrl() { + return AUTHORIZE_URL; } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/SinaWeiboApi20.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/SinaWeiboApi20.java index edd316f87..19aa9f356 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/SinaWeiboApi20.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/SinaWeiboApi20.java @@ -1,7 +1,8 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.builder.api.OAuth2SignatureType; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignature; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignatureURIQueryParameter; /** * SinaWeibo OAuth 2.0 api. @@ -30,7 +31,7 @@ protected String getAuthorizationBaseUrl() { } @Override - public OAuth2SignatureType getSignatureType() { - return OAuth2SignatureType.BEARER_URI_QUERY_PARAMETER; + public BearerSignature getBearerSignature() { + return BearerSignatureURIQueryParameter.instance(); } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/SkyrockApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/SkyrockApi.java index 02c0eb533..beb956a4a 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/SkyrockApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/SkyrockApi.java @@ -1,7 +1,6 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi10a; -import com.github.scribejava.core.model.OAuth1RequestToken; /** * OAuth API for Skyrock. @@ -12,7 +11,7 @@ public class SkyrockApi extends DefaultApi10a { private static final String API_ENDPOINT = "https://api.skyrock.com/v2"; private static final String REQUEST_TOKEN_RESOURCE = "/oauth/initiate"; - private static final String AUTHORIZE_URL = "/oauth/authorize?oauth_token=%s"; + private static final String AUTHORIZE_URL = "/oauth/authorize"; private static final String ACCESS_TOKEN_RESOURCE = "/oauth/token"; protected SkyrockApi() { @@ -37,7 +36,7 @@ public String getRequestTokenEndpoint() { } @Override - public String getAuthorizationUrl(OAuth1RequestToken requestToken) { - return String.format(API_ENDPOINT + AUTHORIZE_URL, requestToken.getToken()); + public String getAuthorizationBaseUrl() { + return API_ENDPOINT + AUTHORIZE_URL; } } 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/SohuWeiboApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/SohuWeiboApi.java deleted file mode 100644 index 6ef769c6a..000000000 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/SohuWeiboApi.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.github.scribejava.apis; - -import com.github.scribejava.core.builder.api.DefaultApi10a; -import com.github.scribejava.core.model.OAuth1RequestToken; - -public class SohuWeiboApi extends DefaultApi10a { - - private static final String REQUEST_TOKEN_URL = "http://api.t.sohu.com/oauth/request_token"; - private static final String ACCESS_TOKEN_URL = "http://api.t.sohu.com/oauth/access_token"; - private static final String AUTHORIZE_URL = "http://api.t.sohu.com/oauth/authorize?oauth_token=%s"; - - protected SohuWeiboApi() { - } - - private static class InstanceHolder { - private static final SohuWeiboApi INSTANCE = new SohuWeiboApi(); - } - - public static SohuWeiboApi instance() { - return InstanceHolder.INSTANCE; - } - - @Override - public String getRequestTokenEndpoint() { - return REQUEST_TOKEN_URL; - } - - @Override - public String getAccessTokenEndpoint() { - return ACCESS_TOKEN_URL; - } - - @Override - public String getAuthorizationUrl(OAuth1RequestToken requestToken) { - return String.format(AUTHORIZE_URL, requestToken.getToken()); - } -} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/StackExchangeApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/StackExchangeApi.java index 554c97b33..3ba195460 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/StackExchangeApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/StackExchangeApi.java @@ -1,10 +1,11 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.builder.api.OAuth2SignatureType; import com.github.scribejava.core.extractors.OAuth2AccessTokenExtractor; import com.github.scribejava.core.extractors.TokenExtractor; import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignature; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignatureURIQueryParameter; /** * Stack Exchange authentication via OAuth 2.0 (stackoverflow.com, @@ -39,7 +40,7 @@ public TokenExtractor getAccessTokenExtractor() { } @Override - public OAuth2SignatureType getSignatureType() { - return OAuth2SignatureType.BEARER_URI_QUERY_PARAMETER; + public BearerSignature getBearerSignature() { + return BearerSignatureURIQueryParameter.instance(); } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/TrelloApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/TrelloApi.java index 8fd95f7a2..438f57402 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/TrelloApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/TrelloApi.java @@ -1,11 +1,10 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi10a; -import com.github.scribejava.core.model.OAuth1RequestToken; public class TrelloApi extends DefaultApi10a { - private static final String AUTHORIZE_URL = "https://trello.com/1/OAuthAuthorizeToken?oauth_token=%s"; + private static final String AUTHORIZE_URL = "https://trello.com/1/OAuthAuthorizeToken"; protected TrelloApi() { } @@ -29,8 +28,8 @@ public String getRequestTokenEndpoint() { } @Override - public String getAuthorizationUrl(OAuth1RequestToken requestToken) { - return String.format(AUTHORIZE_URL, requestToken.getToken()); + public String getAuthorizationBaseUrl() { + return AUTHORIZE_URL; } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/TumblrApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/TumblrApi.java index 1d7a9e93f..111bf3342 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/TumblrApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/TumblrApi.java @@ -1,13 +1,12 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi10a; -import com.github.scribejava.core.model.OAuth1RequestToken; public class TumblrApi extends DefaultApi10a { - private static final String AUTHORIZE_URL = "https://www.tumblr.com/oauth/authorize?oauth_token=%s"; - private static final String REQUEST_TOKEN_RESOURCE = "http://www.tumblr.com/oauth/request_token"; - private static final String ACCESS_TOKEN_RESOURCE = "http://www.tumblr.com/oauth/access_token"; + private static final String AUTHORIZE_URL = "https://www.tumblr.com/oauth/authorize"; + private static final String REQUEST_TOKEN_RESOURCE = "https://www.tumblr.com/oauth/request_token"; + private static final String ACCESS_TOKEN_RESOURCE = "https://www.tumblr.com/oauth/access_token"; protected TumblrApi() { } @@ -31,7 +30,7 @@ public String getRequestTokenEndpoint() { } @Override - public String getAuthorizationUrl(OAuth1RequestToken requestToken) { - return String.format(AUTHORIZE_URL, requestToken.getToken()); + public String getAuthorizationBaseUrl() { + return AUTHORIZE_URL; } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/TutByApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/TutByApi.java index 6928ba70a..41f71b307 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/TutByApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/TutByApi.java @@ -1,9 +1,8 @@ package com.github.scribejava.apis; +import com.github.scribejava.apis.tutby.TutByBearerSignature; import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.model.OAuthConfig; -import com.github.scribejava.apis.service.TutByOAuthServiceImpl; -import com.github.scribejava.core.oauth.OAuth20Service; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignature; public class TutByApi extends DefaultApi20 { @@ -29,7 +28,7 @@ protected String getAuthorizationBaseUrl() { } @Override - public OAuth20Service createService(OAuthConfig config) { - return new TutByOAuthServiceImpl(this, config); + public BearerSignature getBearerSignature() { + return TutByBearerSignature.instance(); } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/TwitterApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/TwitterApi.java index 3d8a2aba7..d18c22b16 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/TwitterApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/TwitterApi.java @@ -1,11 +1,10 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi10a; -import com.github.scribejava.core.model.OAuth1RequestToken; public class TwitterApi extends DefaultApi10a { - private static final String AUTHORIZE_URL = "https://api.twitter.com/oauth/authorize?oauth_token=%s"; + private static final String AUTHORIZE_URL = "https://api.twitter.com/oauth/authorize"; private static final String REQUEST_TOKEN_RESOURCE = "api.twitter.com/oauth/request_token"; private static final String ACCESS_TOKEN_RESOURCE = "api.twitter.com/oauth/access_token"; @@ -31,8 +30,8 @@ public String getRequestTokenEndpoint() { } @Override - public String getAuthorizationUrl(OAuth1RequestToken requestToken) { - return String.format(AUTHORIZE_URL, requestToken.getToken()); + public String getAuthorizationBaseUrl() { + return AUTHORIZE_URL; } /** @@ -42,7 +41,7 @@ public String getAuthorizationUrl(OAuth1RequestToken requestToken) { */ public static class Authenticate extends TwitterApi { - private static final String AUTHENTICATE_URL = "https://api.twitter.com/oauth/authenticate?oauth_token=%s"; + private static final String AUTHENTICATE_URL = "https://api.twitter.com/oauth/authenticate"; private Authenticate() { } @@ -56,8 +55,8 @@ public static Authenticate instance() { } @Override - public String getAuthorizationUrl(OAuth1RequestToken requestToken) { - return String.format(AUTHENTICATE_URL, requestToken.getToken()); + public String getAuthorizationBaseUrl() { + return AUTHENTICATE_URL; } } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/UcozApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/UcozApi.java new file mode 100644 index 000000000..1a8992704 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/UcozApi.java @@ -0,0 +1,48 @@ +package com.github.scribejava.apis; + +import com.github.scribejava.core.extractors.OAuth1AccessTokenJSONExtractor; +import com.github.scribejava.core.extractors.OAuth1RequestTokenJSONExtractor; +import com.github.scribejava.core.builder.api.DefaultApi10a; +import com.github.scribejava.core.extractors.TokenExtractor; +import com.github.scribejava.core.model.OAuth1AccessToken; +import com.github.scribejava.core.model.OAuth1RequestToken; + +public class UcozApi extends DefaultApi10a { + private static final String AUTHORIZE_URL = "http://uapi.ucoz.com/accounts/oauthauthorizetoken"; + + protected UcozApi() { + } + + private static class InstanceHolder { + private static final UcozApi INSTANCE = new UcozApi(); + } + + public static UcozApi instance() { + return InstanceHolder.INSTANCE; + } + + @Override + public String getAccessTokenEndpoint(){ + return "http://uapi.ucoz.com/accounts/oauthgetaccesstoken"; + } + + @Override + public String getRequestTokenEndpoint() { + return "http://uapi.ucoz.com/accounts/oauthgetrequesttoken"; + } + + @Override + public String getAuthorizationBaseUrl() { + return AUTHORIZE_URL; + } + + @Override + public TokenExtractor getAccessTokenExtractor() { + return OAuth1AccessTokenJSONExtractor.instance(); + } + + @Override + public TokenExtractor getRequestTokenExtractor() { + return OAuth1RequestTokenJSONExtractor.instance(); + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/ViadeoApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/ViadeoApi.java index 30c0ffcfe..fb70de258 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/ViadeoApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/ViadeoApi.java @@ -1,8 +1,9 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.builder.api.OAuth2SignatureType; import com.github.scribejava.core.model.Verb; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignature; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignatureURIQueryParameter; public class ViadeoApi extends DefaultApi20 { @@ -33,7 +34,7 @@ protected String getAuthorizationBaseUrl() { } @Override - public OAuth2SignatureType getSignatureType() { - return OAuth2SignatureType.BEARER_URI_QUERY_PARAMETER; + public BearerSignature getBearerSignature() { + return BearerSignatureURIQueryParameter.instance(); } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/VkontakteApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/VkontakteApi.java index 285cb2cde..dfc96275b 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/VkontakteApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/VkontakteApi.java @@ -1,15 +1,24 @@ package com.github.scribejava.apis; +import com.github.scribejava.apis.vk.VKJsonTokenExtractor; import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.builder.api.OAuth2SignatureType; +import com.github.scribejava.core.extractors.TokenExtractor; +import com.github.scribejava.core.model.OAuth2AccessToken; import com.github.scribejava.core.model.Verb; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignature; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignatureURIQueryParameter; +import com.github.scribejava.core.oauth2.clientauthentication.ClientAuthentication; +import com.github.scribejava.core.oauth2.clientauthentication.RequestBodyAuthenticationScheme; public class VkontakteApi extends DefaultApi20 { + public static final String VERSION = "5.92"; + protected VkontakteApi() { } private static class InstanceHolder { + private static final VkontakteApi INSTANCE = new VkontakteApi(); } @@ -29,11 +38,21 @@ public String getAccessTokenEndpoint() { @Override protected String getAuthorizationBaseUrl() { - return "https://oauth.vk.com/authorize"; + return "https://oauth.vk.com/authorize?v=" + VERSION; + } + + @Override + public BearerSignature getBearerSignature() { + return BearerSignatureURIQueryParameter.instance(); + } + + @Override + public ClientAuthentication getClientAuthentication() { + return RequestBodyAuthenticationScheme.instance(); } @Override - public OAuth2SignatureType getSignatureType() { - return OAuth2SignatureType.BEARER_URI_QUERY_PARAMETER; + public TokenExtractor getAccessTokenExtractor() { + return VKJsonTokenExtractor.instance(); } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/WunderlistAPI.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/WunderlistAPI.java new file mode 100644 index 000000000..abf19db92 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/WunderlistAPI.java @@ -0,0 +1,58 @@ +package com.github.scribejava.apis; + +import java.io.OutputStream; + +import com.github.scribejava.apis.wunderlist.WunderlistOAuthService; +import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignature; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignatureURIQueryParameter; +import com.github.scribejava.core.oauth2.clientauthentication.ClientAuthentication; +import com.github.scribejava.core.oauth2.clientauthentication.RequestBodyAuthenticationScheme; + +/** + * Wunderlist.com Api + */ +public class WunderlistAPI extends DefaultApi20 { + + protected WunderlistAPI() { + } + + private static class InstanceHolder { + + private static final WunderlistAPI INSTANCE = new WunderlistAPI(); + } + + public static WunderlistAPI instance() { + return InstanceHolder.INSTANCE; + } + + @Override + public String getAccessTokenEndpoint() { + return "https://www.wunderlist.com/oauth/access_token"; + } + + @Override + protected String getAuthorizationBaseUrl() { + return "https://www.wunderlist.com/oauth/authorize"; + } + + @Override + public BearerSignature getBearerSignature() { + return BearerSignatureURIQueryParameter.instance(); + } + + @Override + public ClientAuthentication getClientAuthentication() { + return RequestBodyAuthenticationScheme.instance(); + } + + @Override + public WunderlistOAuthService createService(String apiKey, String apiSecret, String callback, String defaultScope, + String responseType, OutputStream debugStream, String userAgent, HttpClientConfig httpClientConfig, + HttpClient httpClient) { + return new WunderlistOAuthService(this, apiKey, apiSecret, callback, defaultScope, responseType, debugStream, + userAgent, httpClientConfig, httpClient); + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/XeroApi20.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/XeroApi20.java new file mode 100644 index 000000000..2141ed007 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/XeroApi20.java @@ -0,0 +1,31 @@ +package com.github.scribejava.apis; + +import com.github.scribejava.core.builder.api.DefaultApi20; + +/** + * Xero.com Api + */ +public class XeroApi20 extends DefaultApi20 { + + protected XeroApi20() { + } + + private static class InstanceHolder { + + private static final XeroApi20 INSTANCE = new XeroApi20(); + } + + public static XeroApi20 instance() { + return InstanceHolder.INSTANCE; + } + + @Override + public String getAccessTokenEndpoint() { + return "https://identity.xero.com/connect/token"; + } + + @Override + protected String getAuthorizationBaseUrl() { + return "https://login.xero.com/identity/connect/authorize"; + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/XingApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/XingApi.java index 5bef0670b..b83da3679 100755 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/XingApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/XingApi.java @@ -1,11 +1,10 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi10a; -import com.github.scribejava.core.model.OAuth1RequestToken; public class XingApi extends DefaultApi10a { - private static final String AUTHORIZE_URL = "https://api.xing.com/v1/authorize?oauth_token=%s"; + private static final String AUTHORIZE_URL = "https://api.xing.com/v1/authorize"; protected XingApi() { } @@ -29,8 +28,8 @@ public String getRequestTokenEndpoint() { } @Override - public String getAuthorizationUrl(OAuth1RequestToken requestToken) { - return String.format(AUTHORIZE_URL, requestToken.getToken()); + public String getAuthorizationBaseUrl() { + return AUTHORIZE_URL; } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/YahooApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/YahooApi.java index 18352fdb0..6f3be2038 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/YahooApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/YahooApi.java @@ -1,11 +1,10 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi10a; -import com.github.scribejava.core.model.OAuth1RequestToken; public class YahooApi extends DefaultApi10a { - private static final String AUTHORIZE_URL = "https://api.login.yahoo.com/oauth/v2/request_auth?oauth_token=%s"; + private static final String AUTHORIZE_URL = "https://api.login.yahoo.com/oauth/v2/request_auth"; protected YahooApi() { } @@ -29,7 +28,7 @@ public String getRequestTokenEndpoint() { } @Override - public String getAuthorizationUrl(OAuth1RequestToken requestToken) { - return String.format(AUTHORIZE_URL, requestToken.getToken()); + public String getAuthorizationBaseUrl() { + return AUTHORIZE_URL; } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/YahooApi20.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/YahooApi20.java new file mode 100644 index 000000000..bc50f6779 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/YahooApi20.java @@ -0,0 +1,27 @@ +package com.github.scribejava.apis; + +import com.github.scribejava.core.builder.api.DefaultApi20; + +public class YahooApi20 extends DefaultApi20 { + + protected YahooApi20() { + } + + private static class InstanceHolder { + private static final YahooApi20 INSTANCE = new YahooApi20(); + } + + public static YahooApi20 instance() { + return InstanceHolder.INSTANCE; + } + + @Override + public String getAccessTokenEndpoint() { + return "https://api.login.yahoo.com/oauth2/get_token"; + } + + @Override + protected String getAuthorizationBaseUrl() { + return "https://api.login.yahoo.com/oauth2/request_auth"; + } +} 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 30c049a3f..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,47 +16,47 @@ * '{"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 String code; + private final int codeInt; private final String fbtraceId; - private final String rawResponse; - public FacebookAccessTokenErrorResponse(String message, String type, String 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.code = code; + this.codeInt = code; this.fbtraceId = fbtraceId; - this.rawResponse = rawResponse; + } + + public String getErrorMessage() { + return errorMessage; } public String getType() { return type; } - public String getCode() { - return code; + public int getCodeInt() { + return codeInt; } 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(code); + hash = 83 * hash + Objects.hashCode(codeInt); hash = 83 * hash + Objects.hashCode(fbtraceId); return hash; } @@ -70,17 +72,19 @@ 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())) { return false; } - if (!Objects.equals(code, other.getCode())) { + if (codeInt != other.getCodeInt()) { return false; } return Objects.equals(fbtraceId, other.getFbtraceId()); @@ -88,8 +92,8 @@ public boolean equals(Object obj) { @Override public String toString() { - return "FacebookAccessTokenErrorResponse{'type'='" + type + "', 'code'='" + code - + "', 'fbtraceId'='" + fbtraceId + "', 'rawResponse'='" + rawResponse - + "', 'message'='" + getMessage() + "'}"; + return "FacebookAccessTokenErrorResponse{'type'='" + type + "', 'codeInt'='" + codeInt + + "', '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 5f6d71d44..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 @@ -1,18 +1,15 @@ package com.github.scribejava.apis.facebook; +import com.fasterxml.jackson.databind.JsonNode; import com.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor; -import java.util.regex.Pattern; +import com.github.scribejava.core.model.Response; +import java.io.IOException; /** * non standard Facebook Extractor */ public class FacebookAccessTokenJsonExtractor extends OAuth2AccessTokenJsonExtractor { - private static final Pattern MESSAGE_REGEX_PATTERN = Pattern.compile("\"message\"\\s*:\\s*\"([^\"]*?)\""); - private static final Pattern TYPE_REGEX_PATTERN = Pattern.compile("\"type\"\\s*:\\s*\"([^\"]*?)\""); - private static final Pattern CODE_REGEX_PATTERN = Pattern.compile("\"code\"\\s*:\\s*\"?([^\",}]*?)[\",}]"); - private static final Pattern FBTRACE_ID_REGEX_PATTERN = Pattern.compile("\"fbtrace_id\"\\s*:\\s*\"([^\"]*?)\""); - protected FacebookAccessTokenJsonExtractor() { } @@ -33,15 +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 - protected void generateError(String response) { - extractParameter(response, MESSAGE_REGEX_PATTERN, false); + public void generateError(Response response) throws IOException { + final JsonNode errorNode = OAuth2AccessTokenJsonExtractor.OBJECT_MAPPER + .readTree(response.getBody()) + .get("error"); - throw new FacebookAccessTokenErrorResponse(extractParameter(response, MESSAGE_REGEX_PATTERN, false), - extractParameter(response, TYPE_REGEX_PATTERN, false), - extractParameter(response, CODE_REGEX_PATTERN, false), - extractParameter(response, FBTRACE_ID_REGEX_PATTERN, false), response); + throw new FacebookAccessTokenErrorResponse(errorNode.get("message").asText(), errorNode.get("type").asText(), + errorNode.get("code").asInt(), errorNode.get("fbtrace_id").asText(), response); } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/facebook/FacebookService.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/facebook/FacebookService.java new file mode 100644 index 000000000..efb53a4f3 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/facebook/FacebookService.java @@ -0,0 +1,46 @@ +package com.github.scribejava.apis.facebook; + +import com.github.scribejava.apis.FacebookApi; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; +import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.oauth.OAuth20Service; + +import java.io.OutputStream; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Formatter; +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +public class FacebookService extends OAuth20Service { + + public FacebookService(FacebookApi 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); + } + + @Override + public void signRequest(String accessToken, OAuthRequest request) { + super.signRequest(accessToken, request); + + final Mac mac; + try { + mac = Mac.getInstance("HmacSHA256"); + final SecretKeySpec secretKey = new SecretKeySpec(getApiSecret().getBytes(), "HmacSHA256"); + mac.init(secretKey); + + final Formatter appsecretProof = new Formatter(); + + for (byte b : mac.doFinal(accessToken.getBytes())) { + appsecretProof.format("%02x", b); + } + + request.addParameter("appsecret_proof", appsecretProof.toString()); + } catch (NoSuchAlgorithmException | InvalidKeyException e) { + throw new IllegalStateException("There is a problem while generating Facebook appsecret_proof.", e); + } + } +} 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 new file mode 100644 index 000000000..24ed6025a --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/fitbit/FitBitJsonTokenExtractor.java @@ -0,0 +1,55 @@ +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; + +public class FitBitJsonTokenExtractor extends OAuth2AccessTokenJsonExtractor { + + protected FitBitJsonTokenExtractor() { + } + + private static class InstanceHolder { + + private static final FitBitJsonTokenExtractor INSTANCE = new FitBitJsonTokenExtractor(); + } + + public static FitBitJsonTokenExtractor instance() { + return InstanceHolder.INSTANCE; + } + + @Override + protected FitBitOAuth2AccessToken createToken(String accessToken, String tokenType, Integer expiresIn, + String refreshToken, String scope, JsonNode response, String rawResponse) { + return new FitBitOAuth2AccessToken(accessToken, tokenType, expiresIn, refreshToken, scope, + response.get("user_id").asText(), rawResponse); + } + + /** + * Related documentation: https://dev.fitbit.com/build/reference/web-api/oauth2/ + */ + @Override + 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", response.getBody()).asText()); + } catch (IllegalArgumentException iaE) { + //non oauth standard error code + errorCode = null; + } + + throw new OAuth2AccessTokenErrorResponse(errorCode, errorNode.get("message").asText(), null, response); + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/fitbit/FitBitOAuth2AccessToken.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/fitbit/FitBitOAuth2AccessToken.java new file mode 100644 index 000000000..018128bde --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/fitbit/FitBitOAuth2AccessToken.java @@ -0,0 +1,50 @@ +package com.github.scribejava.apis.fitbit; + +import com.github.scribejava.core.model.OAuth2AccessToken; +import java.util.Objects; + +public class FitBitOAuth2AccessToken extends OAuth2AccessToken { + + private static final long serialVersionUID = -6374486860742407411L; + + private final String userId; + + public FitBitOAuth2AccessToken(String accessToken, String openIdToken, String rawResponse) { + this(accessToken, null, null, null, null, openIdToken, rawResponse); + } + + public FitBitOAuth2AccessToken(String accessToken, String tokenType, Integer expiresIn, String refreshToken, + String scope, String userId, String rawResponse) { + super(accessToken, tokenType, expiresIn, refreshToken, scope, rawResponse); + this.userId = userId; + } + + public String getUserId() { + return userId; + } + + @Override + public int hashCode() { + int hash = super.hashCode(); + hash = 37 * hash + Objects.hashCode(userId); + 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(userId, ((FitBitOAuth2AccessToken) obj).getUserId()); + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/google/GoogleDeviceAuthorizationJsonExtractor.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/google/GoogleDeviceAuthorizationJsonExtractor.java new file mode 100644 index 000000000..d3dd77772 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/google/GoogleDeviceAuthorizationJsonExtractor.java @@ -0,0 +1,25 @@ +package com.github.scribejava.apis.google; + +import com.github.scribejava.core.extractors.DeviceAuthorizationJsonExtractor; + +public class GoogleDeviceAuthorizationJsonExtractor extends DeviceAuthorizationJsonExtractor { + + protected GoogleDeviceAuthorizationJsonExtractor() { + } + + private static class InstanceHolder { + + private static final GoogleDeviceAuthorizationJsonExtractor INSTANCE + = new GoogleDeviceAuthorizationJsonExtractor(); + } + + public static GoogleDeviceAuthorizationJsonExtractor instance() { + return GoogleDeviceAuthorizationJsonExtractor.InstanceHolder.INSTANCE; + } + + @Override + protected String getVerificationUriParamName() { + return "verification_url"; + } + +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/google/GoogleJsonTokenExtractor.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/google/GoogleJsonTokenExtractor.java deleted file mode 100644 index a343e7f63..000000000 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/google/GoogleJsonTokenExtractor.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.github.scribejava.apis.google; - -import com.github.scribejava.apis.openid.OpenIdJsonTokenExtractor; -import java.util.regex.Pattern; - -/** - * - * @deprecated use generic {@link OpenIdJsonTokenExtractor} - */ -@Deprecated -public class GoogleJsonTokenExtractor extends OpenIdJsonTokenExtractor { - - private static final Pattern ID_TOKEN_REGEX_PATTERN = Pattern.compile("\"id_token\"\\s*:\\s*\"(\\S*?)\""); - - protected GoogleJsonTokenExtractor() { - } - - private static class InstanceHolder { - - private static final GoogleJsonTokenExtractor INSTANCE = new GoogleJsonTokenExtractor(); - } - - public static GoogleJsonTokenExtractor instance() { - return InstanceHolder.INSTANCE; - } - - @Override - protected GoogleToken createToken(String accessToken, String tokenType, Integer expiresIn, - String refreshToken, String scope, String response) { - return new GoogleToken(accessToken, tokenType, expiresIn, refreshToken, scope, - extractParameter(response, ID_TOKEN_REGEX_PATTERN, false), response); - } -} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/google/GoogleToken.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/google/GoogleToken.java deleted file mode 100644 index aa76087b8..000000000 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/google/GoogleToken.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.github.scribejava.apis.google; - -import com.github.scribejava.apis.openid.OpenIdOAuth2AccessToken; - -/** - * @deprecated use generic {@link OpenIdOAuth2AccessToken} - */ -@Deprecated -public class GoogleToken extends OpenIdOAuth2AccessToken { - - private static final long serialVersionUID = -5959403983480821444L; - - public GoogleToken(String accessToken, String openIdToken, String rawResponse) { - super(accessToken, null, null, null, null, openIdToken, rawResponse); - } - - public GoogleToken(String accessToken, String tokenType, Integer expiresIn, String refreshToken, String scope, - String openIdToken, String rawResponse) { - super(accessToken, tokenType, expiresIn, refreshToken, scope, openIdToken, rawResponse); - } -} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/imgur/ImgurOAuthService.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/imgur/ImgurOAuthService.java new file mode 100644 index 000000000..317934e35 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/imgur/ImgurOAuthService.java @@ -0,0 +1,53 @@ +package com.github.scribejava.apis.imgur; + +import java.io.OutputStream; + +import com.github.scribejava.apis.ImgurApi; +import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; +import com.github.scribejava.core.model.OAuthConstants; +import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.oauth.AccessTokenRequestParams; +import com.github.scribejava.core.oauth.OAuth20Service; +import com.github.scribejava.core.pkce.PKCE; + +public class ImgurOAuthService extends OAuth20Service { + + public ImgurOAuthService(ImgurApi 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); + } + + @Override + protected OAuthRequest createAccessTokenRequest(AccessTokenRequestParams params) { + final DefaultApi20 api = getApi(); + final OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint()); + request.addBodyParameter(OAuthConstants.CLIENT_ID, getApiKey()); + request.addBodyParameter(OAuthConstants.CLIENT_SECRET, getApiSecret()); + + final String oauthVerifier = params.getCode(); + if (ImgurApi.isOob(getCallback())) { + request.addBodyParameter(OAuthConstants.GRANT_TYPE, "pin"); + request.addBodyParameter("pin", oauthVerifier); + } else { + request.addBodyParameter(OAuthConstants.GRANT_TYPE, OAuthConstants.AUTHORIZATION_CODE); + request.addBodyParameter(OAuthConstants.CODE, oauthVerifier); + } + + final String pkceCodeVerifier = params.getPkceCodeVerifier(); + if (pkceCodeVerifier != null) { + request.addParameter(PKCE.PKCE_CODE_VERIFIER_PARAM, pkceCodeVerifier); + } + + return request; + } + + @Override + public void signRequest(String accessToken, OAuthRequest request) { + request.addHeader(OAuthConstants.HEADER, + accessToken == null ? "Client-ID " + getApiKey() : "Bearer " + accessToken); + } +} 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/mailru/MailruOAuthService.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/mailru/MailruOAuthService.java new file mode 100644 index 000000000..a9ff7d77b --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/mailru/MailruOAuthService.java @@ -0,0 +1,74 @@ +package com.github.scribejava.apis.mailru; + +import com.github.scribejava.apis.MailruApi; +import java.net.URLDecoder; +import java.util.Map; +import java.util.TreeMap; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; +import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.oauth.OAuth20Service; + +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Formatter; + +public class MailruOAuthService extends OAuth20Service { + + public MailruOAuthService(MailruApi 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); + } + + @Override + public void signRequest(String accessToken, OAuthRequest request) { + // sig = md5(params + secret_key) + request.addQuerystringParameter("session_key", accessToken); + request.addQuerystringParameter("app_id", getApiKey()); + final String completeUrl = request.getCompleteUrl(); + + try { + final String clientSecret = getApiSecret(); + final int queryIndex = completeUrl.indexOf('?'); + if (queryIndex != -1) { + final String urlPart = completeUrl.substring(queryIndex + 1); + final Map map = new TreeMap<>(); + for (String param : urlPart.split("&")) { + final String[] parts = param.split("="); + map.put(parts[0], (parts.length == 1) ? "" : parts[1]); + } + + final StringBuilder urlNew = new StringBuilder(); + for (Map.Entry entry : map.entrySet()) { + urlNew.append(entry.getKey()); + urlNew.append('='); + urlNew.append(entry.getValue()); + } + + final String sigSource = URLDecoder.decode(urlNew.toString(), "UTF-8") + clientSecret; + request.addQuerystringParameter("sig", md5(sigSource)); + } + } catch (UnsupportedEncodingException e) { + throw new IllegalStateException(e); + } + } + + public static String md5(String orgString) { + try { + final MessageDigest md = MessageDigest.getInstance("MD5"); + final byte[] array = md.digest(orgString.getBytes(Charset.forName("UTF-8"))); + final Formatter builder = new Formatter(); + for (byte b : array) { + builder.format("%02x", b); + } + return builder.toString(); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("MD5 is unsupported?", e); + } + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/microsoftazureactivedirectory/BaseMicrosoftAzureActiveDirectoryApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/microsoftazureactivedirectory/BaseMicrosoftAzureActiveDirectoryApi.java new file mode 100644 index 000000000..5e4891164 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/microsoftazureactivedirectory/BaseMicrosoftAzureActiveDirectoryApi.java @@ -0,0 +1,41 @@ +package com.github.scribejava.apis.microsoftazureactivedirectory; + +import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.oauth2.clientauthentication.ClientAuthentication; +import com.github.scribejava.core.oauth2.clientauthentication.RequestBodyAuthenticationScheme; + +public abstract class BaseMicrosoftAzureActiveDirectoryApi extends DefaultApi20 { + + protected static final String COMMON_TENANT = "common"; + + private static final String MSFT_LOGIN_URL = "https://login.microsoftonline.com/"; + private static final String OAUTH_2 = "/oauth2"; + private final String tenant; + + protected BaseMicrosoftAzureActiveDirectoryApi() { + this(COMMON_TENANT); + } + + protected BaseMicrosoftAzureActiveDirectoryApi(String tenant) { + this.tenant = tenant == null || tenant.isEmpty() ? COMMON_TENANT : tenant; + } + + @Override + public String getAccessTokenEndpoint() { + return MSFT_LOGIN_URL + tenant + OAUTH_2 + getEndpointVersionPath() + "/token"; + } + + @Override + protected String getAuthorizationBaseUrl() { + return MSFT_LOGIN_URL + tenant + OAUTH_2 + getEndpointVersionPath() + "/authorize"; + } + + @Override + public ClientAuthentication getClientAuthentication() { + return RequestBodyAuthenticationScheme.instance(); + } + + protected String getEndpointVersionPath() { + return ""; + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/microsoftazureactivedirectory/BaseMicrosoftAzureActiveDirectoryBearerSignature.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/microsoftazureactivedirectory/BaseMicrosoftAzureActiveDirectoryBearerSignature.java new file mode 100644 index 000000000..ab85674af --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/microsoftazureactivedirectory/BaseMicrosoftAzureActiveDirectoryBearerSignature.java @@ -0,0 +1,20 @@ +package com.github.scribejava.apis.microsoftazureactivedirectory; + +import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignatureAuthorizationRequestHeaderField; + +public abstract class BaseMicrosoftAzureActiveDirectoryBearerSignature + extends BearerSignatureAuthorizationRequestHeaderField { + + private final String acceptedFormat; + + protected BaseMicrosoftAzureActiveDirectoryBearerSignature(String acceptedFormat) { + this.acceptedFormat = acceptedFormat; + } + + @Override + public void signRequest(String accessToken, OAuthRequest request) { + super.signRequest(accessToken, request); + request.addHeader("Accept", acceptedFormat); + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/microsoftazureactivedirectory/MicrosoftAzureActiveDirectory20BearerSignature.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/microsoftazureactivedirectory/MicrosoftAzureActiveDirectory20BearerSignature.java new file mode 100644 index 000000000..c554beeff --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/microsoftazureactivedirectory/MicrosoftAzureActiveDirectory20BearerSignature.java @@ -0,0 +1,18 @@ +package com.github.scribejava.apis.microsoftazureactivedirectory; + +public class MicrosoftAzureActiveDirectory20BearerSignature extends BaseMicrosoftAzureActiveDirectoryBearerSignature { + + protected MicrosoftAzureActiveDirectory20BearerSignature() { + super("application/json"); + } + + private static class InstanceHolder { + + private static final MicrosoftAzureActiveDirectory20BearerSignature INSTANCE + = new MicrosoftAzureActiveDirectory20BearerSignature(); + } + + public static MicrosoftAzureActiveDirectory20BearerSignature instance() { + return InstanceHolder.INSTANCE; + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/microsoftazureactivedirectory/MicrosoftAzureActiveDirectoryBearerSignature.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/microsoftazureactivedirectory/MicrosoftAzureActiveDirectoryBearerSignature.java new file mode 100644 index 000000000..bfddd523a --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/microsoftazureactivedirectory/MicrosoftAzureActiveDirectoryBearerSignature.java @@ -0,0 +1,18 @@ +package com.github.scribejava.apis.microsoftazureactivedirectory; + +public class MicrosoftAzureActiveDirectoryBearerSignature extends BaseMicrosoftAzureActiveDirectoryBearerSignature { + + protected MicrosoftAzureActiveDirectoryBearerSignature() { + super("application/json; odata=minimalmetadata; streaming=true; charset=utf-8"); + } + + private static class InstanceHolder { + + private static final MicrosoftAzureActiveDirectoryBearerSignature INSTANCE + = new MicrosoftAzureActiveDirectoryBearerSignature(); + } + + public static MicrosoftAzureActiveDirectoryBearerSignature instance() { + return InstanceHolder.INSTANCE; + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/odnoklassniki/OdnoklassnikiOAuthService.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/odnoklassniki/OdnoklassnikiOAuthService.java new file mode 100644 index 000000000..fd1327a6d --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/odnoklassniki/OdnoklassnikiOAuthService.java @@ -0,0 +1,72 @@ +package com.github.scribejava.apis.odnoklassniki; + +import com.github.scribejava.apis.OdnoklassnikiApi; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; +import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.model.Parameter; +import com.github.scribejava.core.model.ParameterList; +import com.github.scribejava.core.oauth.OAuth20Service; + +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; + +import java.net.URLDecoder; +import java.nio.charset.Charset; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Collections; +import java.util.Formatter; +import java.util.List; + +public class OdnoklassnikiOAuthService extends OAuth20Service { + + public OdnoklassnikiOAuthService(OdnoklassnikiApi 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); + } + + @Override + public void signRequest(String accessToken, OAuthRequest request) { + //sig = lower(md5( sorted_request_params_composed_string + md5(access_token + application_secret_key))) + try { + final String tokenDigest = md5(accessToken + getApiSecret()); + + final ParameterList queryParams = request.getQueryStringParams(); + queryParams.addAll(request.getBodyParams()); + final List allParams = queryParams.getParams(); + + Collections.sort(allParams); + + final StringBuilder stringParams = new StringBuilder(); + for (Parameter param : allParams) { + stringParams.append(param.getKey()) + .append('=') + .append(param.getValue()); + } + + final String sigSource = URLDecoder.decode(stringParams.toString(), "UTF-8") + tokenDigest; + request.addQuerystringParameter("sig", md5(sigSource).toLowerCase()); + + super.signRequest(accessToken, request); + } catch (UnsupportedEncodingException unex) { + throw new IllegalStateException(unex); + } + } + + public static String md5(String orgString) { + try { + final MessageDigest md = MessageDigest.getInstance("MD5"); + final byte[] array = md.digest(orgString.getBytes(Charset.forName("UTF-8"))); + final Formatter builder = new Formatter(); + for (byte b : array) { + builder.format("%02x", b); + } + return builder.toString(); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("MD5 is unsupported?", e); + } + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/openid/OpenIdJsonTokenExtractor.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/openid/OpenIdJsonTokenExtractor.java index bec8b0613..1fc441a70 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/openid/OpenIdJsonTokenExtractor.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/openid/OpenIdJsonTokenExtractor.java @@ -1,15 +1,13 @@ package com.github.scribejava.apis.openid; +import com.fasterxml.jackson.databind.JsonNode; import com.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor; -import java.util.regex.Pattern; /** * additionally parses OpenID id_token */ public class OpenIdJsonTokenExtractor extends OAuth2AccessTokenJsonExtractor { - private static final Pattern ID_TOKEN_REGEX_PATTERN = Pattern.compile("\"id_token\"\\s*:\\s*\"(\\S*?)\""); - protected OpenIdJsonTokenExtractor() { } @@ -24,8 +22,9 @@ public static OpenIdJsonTokenExtractor instance() { @Override protected OpenIdOAuth2AccessToken createToken(String accessToken, String tokenType, Integer expiresIn, - String refreshToken, String scope, String response) { + String refreshToken, String scope, JsonNode response, String rawResponse) { + final JsonNode idToken = response.get("id_token"); return new OpenIdOAuth2AccessToken(accessToken, tokenType, expiresIn, refreshToken, scope, - extractParameter(response, ID_TOKEN_REGEX_PATTERN, false), response); + idToken == null ? null : idToken.asText(), rawResponse); } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/openid/OpenIdOAuth2AccessToken.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/openid/OpenIdOAuth2AccessToken.java index 94550ef79..7e6588444 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/openid/OpenIdOAuth2AccessToken.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/openid/OpenIdOAuth2AccessToken.java @@ -56,15 +56,4 @@ public boolean equals(Object obj) { return Objects.equals(openIdToken, ((OpenIdOAuth2AccessToken) obj).getOpenIdToken()); } - - @Override - public String toString() { - return "OpenIdOAuth2AccessToken{" - + "access_token=" + getAccessToken() - + ", token_type=" + getTokenType() - + ", expires_in=" + getExpiresIn() - + ", refresh_token=" + getRefreshToken() - + ", scope=" + getScope() - + ", open_id_token=" + openIdToken + '}'; - } } 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 new file mode 100644 index 000000000..1258dfe59 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/polar/PolarJsonTokenExtractor.java @@ -0,0 +1,55 @@ +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; + +/** + * Token related documentation: https://www.polar.com/accesslink-api/#token-endpoint + */ +public class PolarJsonTokenExtractor extends OAuth2AccessTokenJsonExtractor { + + protected PolarJsonTokenExtractor() { + } + + private static class InstanceHolder { + + private static final PolarJsonTokenExtractor INSTANCE = new PolarJsonTokenExtractor(); + } + + public static PolarJsonTokenExtractor instance() { + return InstanceHolder.INSTANCE; + } + + @Override + protected PolarOAuth2AccessToken createToken(String accessToken, String tokenType, Integer expiresIn, + String refreshToken, String scope, JsonNode response, String rawResponse) { + return new PolarOAuth2AccessToken(accessToken, tokenType, expiresIn, refreshToken, scope, + response.get("x_user_id").asText(), rawResponse); + } + + @Override + 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", response.getBody()).asText()); + } catch (IllegalArgumentException iaE) { + //non oauth standard error code + errorCode = null; + } + + throw new OAuth2AccessTokenErrorResponse(errorCode, errorNode.get("message").asText(), null, response); + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/polar/PolarOAuth2AccessToken.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/polar/PolarOAuth2AccessToken.java new file mode 100644 index 000000000..99710728f --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/polar/PolarOAuth2AccessToken.java @@ -0,0 +1,47 @@ +package com.github.scribejava.apis.polar; + +import com.github.scribejava.core.model.OAuth2AccessToken; + +import java.util.Objects; + +public class PolarOAuth2AccessToken extends OAuth2AccessToken { + + private static final long serialVersionUID = 1L; + + private final String userId; + + public PolarOAuth2AccessToken(String accessToken, String tokenType, Integer expiresIn, String refreshToken, + String scope, String userId, String rawResponse) { + super(accessToken, tokenType, expiresIn, refreshToken, scope, rawResponse); + this.userId = userId; + } + + public String getUserId() { + return userId; + } + + @Override + public int hashCode() { + int hash = super.hashCode(); + hash = 37 * hash + Objects.hashCode(userId); + 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(userId, ((PolarOAuth2AccessToken) obj).getUserId()); + } +} 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 new file mode 100644 index 000000000..c016d6ac7 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/polar/PolarOAuthService.java @@ -0,0 +1,47 @@ +package com.github.scribejava.apis.polar; + +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; +import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.oauth.AccessTokenRequestParams; +import com.github.scribejava.core.oauth.OAuth20Service; +import com.github.scribejava.core.pkce.PKCE; + +import java.io.OutputStream; + +public class PolarOAuthService extends OAuth20Service { + + 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, + httpClient); + } + + @Override + protected OAuthRequest createAccessTokenRequest(AccessTokenRequestParams params) { + final OAuthRequest request = new OAuthRequest(getApi().getAccessTokenVerb(), getApi().getAccessTokenEndpoint()); + + getApi().getClientAuthentication().addClientAuthentication(request, getApiKey(), getApiSecret()); + + request.addParameter(OAuthConstants.CODE, params.getCode()); + final String callback = getCallback(); + if (callback != null) { + request.addParameter(OAuthConstants.REDIRECT_URI, callback); + } + request.addParameter(OAuthConstants.GRANT_TYPE, OAuthConstants.AUTHORIZATION_CODE); + + final String pkceCodeVerifier = params.getPkceCodeVerifier(); + if (pkceCodeVerifier != null) { + request.addParameter(PKCE.PKCE_CODE_VERIFIER_PARAM, pkceCodeVerifier); + } + if (isDebug()) { + log("created 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/salesforce/SalesforceJsonTokenExtractor.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/salesforce/SalesforceJsonTokenExtractor.java index 841276724..62a3294cf 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/salesforce/SalesforceJsonTokenExtractor.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/salesforce/SalesforceJsonTokenExtractor.java @@ -1,7 +1,7 @@ package com.github.scribejava.apis.salesforce; +import com.fasterxml.jackson.databind.JsonNode; import com.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor; -import java.util.regex.Pattern; /** * This extractor parses in addition to the standard Extractor the instance_url @@ -9,8 +9,6 @@ */ public class SalesforceJsonTokenExtractor extends OAuth2AccessTokenJsonExtractor { - private static final Pattern INSTANCE_URL_REGEX_PATTERN = Pattern.compile("\"instance_url\"\\s*:\\s*\"(\\S*?)\""); - protected SalesforceJsonTokenExtractor() { } @@ -25,8 +23,8 @@ public static SalesforceJsonTokenExtractor instance() { @Override protected SalesforceToken createToken(String accessToken, String tokenType, Integer expiresIn, - String refreshToken, String scope, String response) { + String refreshToken, String scope, JsonNode response, String rawResponse) { return new SalesforceToken(accessToken, tokenType, expiresIn, refreshToken, scope, - extractParameter(response, INSTANCE_URL_REGEX_PATTERN, true), response); + extractRequiredParameter(response, "instance_url", rawResponse).asText(), rawResponse); } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/salesforce/SalesforceToken.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/salesforce/SalesforceToken.java index 14a0911cd..242cb7e31 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/salesforce/SalesforceToken.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/salesforce/SalesforceToken.java @@ -51,15 +51,4 @@ public boolean equals(Object obj) { } return Objects.equals(instanceUrl, ((SalesforceToken) obj).getInstanceUrl()); } - - @Override - public String toString() { - return "SalesforceToken{" - + "access_token=" + getAccessToken() - + ", token_type=" + getTokenType() - + ", expires_in=" + getExpiresIn() - + ", refresh_token=" + getRefreshToken() - + ", scope=" + getScope() - + ", instance_url=" + instanceUrl + '}'; - } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/service/ImgurOAuthServiceImpl.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/service/ImgurOAuthServiceImpl.java deleted file mode 100644 index c0f74aab6..000000000 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/service/ImgurOAuthServiceImpl.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.github.scribejava.apis.service; - -import com.github.scribejava.apis.ImgurApi; -import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.model.OAuth2AccessToken; -import com.github.scribejava.core.model.OAuthConfig; -import com.github.scribejava.core.model.OAuthConstants; -import com.github.scribejava.core.model.OAuthRequest; -import com.github.scribejava.core.oauth.OAuth20Service; - -public class ImgurOAuthServiceImpl extends OAuth20Service { - - public ImgurOAuthServiceImpl(DefaultApi20 api, OAuthConfig config) { - super(api, config); - } - - @Override - protected OAuthRequest createAccessTokenRequest(String oauthVerifier) { - final DefaultApi20 api = getApi(); - final OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint()); - final OAuthConfig config = getConfig(); - request.addBodyParameter(OAuthConstants.CLIENT_ID, config.getApiKey()); - request.addBodyParameter(OAuthConstants.CLIENT_SECRET, config.getApiSecret()); - - if (ImgurApi.isOob(config)) { - request.addBodyParameter(OAuthConstants.GRANT_TYPE, "pin"); - request.addBodyParameter("pin", oauthVerifier); - } else { - request.addBodyParameter(OAuthConstants.GRANT_TYPE, OAuthConstants.AUTHORIZATION_CODE); - request.addBodyParameter(OAuthConstants.CODE, oauthVerifier); - } - return request; - } - - @Override - public void signRequest(OAuth2AccessToken accessToken, OAuthRequest request) { - request.addHeader("Authorization", - accessToken == null - ? "Client-ID " + getConfig().getApiKey() : "Bearer " + accessToken.getAccessToken()); - } -} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/service/MailruOAuthServiceImpl.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/service/MailruOAuthServiceImpl.java deleted file mode 100644 index 3330bb006..000000000 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/service/MailruOAuthServiceImpl.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.github.scribejava.apis.service; - -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.util.Map; -import java.util.TreeMap; -import org.apache.commons.codec.CharEncoding; -import static org.apache.commons.codec.digest.DigestUtils.md5Hex; -import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.model.OAuth2AccessToken; -import com.github.scribejava.core.model.OAuthConfig; -import com.github.scribejava.core.model.OAuthRequest; -import com.github.scribejava.core.oauth.OAuth20Service; - -public class MailruOAuthServiceImpl extends OAuth20Service { - - public MailruOAuthServiceImpl(DefaultApi20 api, OAuthConfig config) { - super(api, config); - } - - @Override - public void signRequest(OAuth2AccessToken accessToken, OAuthRequest request) { - // sig = md5(params + secret_key) - request.addQuerystringParameter("session_key", accessToken.getAccessToken()); - request.addQuerystringParameter("app_id", getConfig().getApiKey()); - final String completeUrl = request.getCompleteUrl(); - - try { - final String clientSecret = getConfig().getApiSecret(); - final int queryIndex = completeUrl.indexOf('?'); - if (queryIndex != -1) { - final String urlPart = completeUrl.substring(queryIndex + 1); - final Map map = new TreeMap<>(); - for (String param : urlPart.split("&")) { - final String[] parts = param.split("="); - map.put(parts[0], (parts.length == 1) ? "" : parts[1]); - } - final StringBuilder urlNew = new StringBuilder(); - for (Map.Entry entry : map.entrySet()) { - urlNew.append(entry.getKey()); - urlNew.append('='); - urlNew.append(entry.getValue()); - } - final String sigSource = URLDecoder.decode(urlNew.toString(), CharEncoding.UTF_8) + clientSecret; - request.addQuerystringParameter("sig", md5Hex(sigSource)); - } - } catch (UnsupportedEncodingException e) { - throw new IllegalStateException(e); - } - } -} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/service/OdnoklassnikiServiceImpl.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/service/OdnoklassnikiServiceImpl.java deleted file mode 100644 index fa9ae2e56..000000000 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/service/OdnoklassnikiServiceImpl.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.github.scribejava.apis.service; - -import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.model.OAuth2AccessToken; -import com.github.scribejava.core.model.OAuthConfig; -import com.github.scribejava.core.model.OAuthRequest; -import com.github.scribejava.core.model.Parameter; -import com.github.scribejava.core.model.ParameterList; -import com.github.scribejava.core.oauth.OAuth20Service; - -import org.apache.commons.codec.CharEncoding; - -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.util.Collections; -import java.util.List; - -import static org.apache.commons.codec.digest.DigestUtils.md5Hex; - -public class OdnoklassnikiServiceImpl extends OAuth20Service { - - public OdnoklassnikiServiceImpl(DefaultApi20 api, OAuthConfig config) { - super(api, config); - } - - @Override - public void signRequest(OAuth2AccessToken accessToken, OAuthRequest request) { - //sig = lower(md5( sorted_request_params_composed_string + md5(access_token + application_secret_key))) - try { - final String tokenDigest = md5Hex(accessToken.getAccessToken() + getConfig().getApiSecret()); - - final ParameterList queryParams = request.getQueryStringParams(); - queryParams.addAll(request.getBodyParams()); - final List allParams = queryParams.getParams(); - - Collections.sort(allParams); - final StringBuilder builder = new StringBuilder(); - for (Parameter param : allParams) { - builder.append(param.getKey()).append('=').append(param.getValue()); - } - - final String sigSource = URLDecoder.decode(builder.toString(), CharEncoding.UTF_8) + tokenDigest; - request.addQuerystringParameter("sig", md5Hex(sigSource).toLowerCase()); - - super.signRequest(accessToken, request); - } catch (UnsupportedEncodingException unex) { - throw new IllegalStateException(unex); - } - } -} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/service/TutByOAuthServiceImpl.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/service/TutByOAuthServiceImpl.java deleted file mode 100644 index f1f14485f..000000000 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/service/TutByOAuthServiceImpl.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.github.scribejava.apis.service; - -import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.model.OAuth2AccessToken; -import com.github.scribejava.core.model.OAuthConfig; -import com.github.scribejava.core.model.OAuthConstants; -import com.github.scribejava.core.model.OAuthRequest; -import com.github.scribejava.core.oauth.OAuth20Service; - -public class TutByOAuthServiceImpl extends OAuth20Service { - - public TutByOAuthServiceImpl(DefaultApi20 api, OAuthConfig config) { - super(api, config); - } - - @Override - public void signRequest(OAuth2AccessToken accessToken, OAuthRequest request) { - request.addQuerystringParameter(OAuthConstants.TOKEN, accessToken.getAccessToken()); - } -} 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/main/java/com/github/scribejava/apis/tutby/TutByBearerSignature.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/tutby/TutByBearerSignature.java new file mode 100644 index 000000000..0c7609073 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/tutby/TutByBearerSignature.java @@ -0,0 +1,25 @@ +package com.github.scribejava.apis.tutby; + +import com.github.scribejava.core.model.OAuthConstants; +import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignature; + +public class TutByBearerSignature implements BearerSignature { + + protected TutByBearerSignature() { + } + + private static class InstanceHolder { + + private static final TutByBearerSignature INSTANCE = new TutByBearerSignature(); + } + + public static TutByBearerSignature instance() { + return InstanceHolder.INSTANCE; + } + + @Override + public void signRequest(String accessToken, OAuthRequest request) { + request.addQuerystringParameter(OAuthConstants.TOKEN, accessToken); + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/vk/VKJsonTokenExtractor.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/vk/VKJsonTokenExtractor.java new file mode 100644 index 000000000..a3d14aacf --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/vk/VKJsonTokenExtractor.java @@ -0,0 +1,30 @@ +package com.github.scribejava.apis.vk; + +import com.fasterxml.jackson.databind.JsonNode; +import com.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor; + +/** + * additionally parses email + */ +public class VKJsonTokenExtractor extends OAuth2AccessTokenJsonExtractor { + + protected VKJsonTokenExtractor() { + } + + private static class InstanceHolder { + + private static final VKJsonTokenExtractor INSTANCE = new VKJsonTokenExtractor(); + } + + public static VKJsonTokenExtractor instance() { + return InstanceHolder.INSTANCE; + } + + @Override + protected VKOAuth2AccessToken createToken(String accessToken, String tokenType, Integer expiresIn, + String refreshToken, String scope, JsonNode response, String rawResponse) { + final JsonNode email = response.get("email"); + return new VKOAuth2AccessToken(accessToken, tokenType, expiresIn, refreshToken, scope, + email == null ? null : email.asText(), rawResponse); + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/vk/VKOAuth2AccessToken.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/vk/VKOAuth2AccessToken.java new file mode 100644 index 000000000..a8e75992b --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/vk/VKOAuth2AccessToken.java @@ -0,0 +1,50 @@ +package com.github.scribejava.apis.vk; + +import com.github.scribejava.core.model.OAuth2AccessToken; +import java.util.Objects; + +public class VKOAuth2AccessToken extends OAuth2AccessToken { + + private static final long serialVersionUID = -3539517142527580499L; + + private final String email; + + public VKOAuth2AccessToken(String accessToken, String email, String rawResponse) { + this(accessToken, null, null, null, null, email, rawResponse); + } + + public VKOAuth2AccessToken(String accessToken, String tokenType, Integer expiresIn, String refreshToken, + String scope, String email, String rawResponse) { + super(accessToken, tokenType, expiresIn, refreshToken, scope, rawResponse); + this.email = email; + } + + public String getEmail() { + return email; + } + + @Override + public int hashCode() { + int hash = super.hashCode(); + hash = 37 * hash + Objects.hashCode(email); + 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(email, ((VKOAuth2AccessToken) obj).getEmail()); + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/wunderlist/WunderlistOAuthService.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/wunderlist/WunderlistOAuthService.java new file mode 100644 index 000000000..06a248d9c --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/wunderlist/WunderlistOAuthService.java @@ -0,0 +1,25 @@ +package com.github.scribejava.apis.wunderlist; + +import java.io.OutputStream; + +import com.github.scribejava.apis.WunderlistAPI; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; +import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.oauth.OAuth20Service; + +public class WunderlistOAuthService extends OAuth20Service { + + public WunderlistOAuthService(WunderlistAPI 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); + } + + @Override + public void signRequest(String accessToken, OAuthRequest request) { + super.signRequest(accessToken, request); + request.addHeader("X-Client-ID", getApiKey()); + } +} 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 new file mode 100644 index 000000000..5f60abcb1 --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/ExampleUtils.java @@ -0,0 +1,55 @@ +package com.github.scribejava.apis; + +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +public class ExampleUtils { + + private ExampleUtils() { + } + + public static void turnOfSSl() { + try { + final TrustManager[] trustAllCerts = new TrustManager[]{new TrustAllCertsManager() + }; + + final SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, trustAllCerts, new java.security.SecureRandom()); + HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + + final HostnameVerifier allHostsValid = new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + }; + + HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid); + } catch (NoSuchAlgorithmException | KeyManagementException e) { + 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/AWeberExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/AWeberExample.java index d304dc6d4..e61110716 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/AWeberExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/AWeberExample.java @@ -12,7 +12,7 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class AWeberExample { +public class AWeberExample { //To get your consumer key/secret, and view API docs, see https://labs.aweber.com/docs private static final String ACCOUNT_RESOURCE_URL = "https://api.aweber.com/1.0/accounts/"; @@ -23,6 +23,7 @@ public final class AWeberExample { private AWeberExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { final OAuth10aService service = new ServiceBuilder(CONSUMER_KEY) .apiSecret(CONSUMER_SECRET) @@ -46,22 +47,22 @@ public static void main(String... args) throws IOException, InterruptedException final String oauthVerifier = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token + // Trade the Request Token and Verifier for the Access Token System.out.println("Trading the Request Token for an Access Token..."); final OAuth1AccessToken accessToken = service.getAccessToken(requestToken, oauthVerifier); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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, ACCOUNT_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = service.execute(request); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getBody()); + try (Response response = service.execute(request)) { + System.out.println("Got it! Lets see what we found..."); + System.out.println(); + System.out.println(response.getBody()); + } System.out.println(); System.out.println("Thats it man! Go and build something awesome with AWeber and ScribeJava! :)"); diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/AsanaExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/AsanaExample.java new file mode 100644 index 000000000..1ab4d3d3c --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/AsanaExample.java @@ -0,0 +1,82 @@ +package com.github.scribejava.apis.examples; + +import com.github.scribejava.apis.Asana20Api; +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.Random; +import java.util.Scanner; +import java.util.concurrent.ExecutionException; + +public class AsanaExample { + + private static final String PROTECTED_RESOURCE_URL = "https://app.asana.com/api/1.0/users/me"; + + private AsanaExample() { + } + + @SuppressWarnings("PMD.SystemPrintln") + public static void main(String... args) throws IOException, InterruptedException, ExecutionException { + final String apiKey = "your client id"; + final String apiSecret = "your client secret"; + final String secretState = "secret" + new Random().nextInt(999_999); + final OAuth20Service service = new ServiceBuilder(apiKey) + .apiSecret(apiSecret) + .callback("https://localhost/") + .build(Asana20Api.instance()); + final Scanner in = new Scanner(System.in); + + // Obtain Auth URL + System.out.println("Fetching the Authorication URL..."); + System.out.println("Got the Authorization URL!"); + final String authorizationUrl = service.getAuthorizationUrl(secretState); + 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..."); + 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("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("Refreshing the Access Token..."); + accessToken = service.refreshAccessToken(accessToken.getRefreshToken()); + System.out.println("Refreshed the Access Token!"); + System.out.println("(The raw response looks like this: " + accessToken.getRawResponse() + "')"); + System.out.println(); + + 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! :)"); + } +} diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/AutomaticExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/AutomaticExample.java new file mode 100644 index 000000000..ae0c299f2 --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/AutomaticExample.java @@ -0,0 +1,83 @@ +package com.github.scribejava.apis.examples; + +import com.github.scribejava.apis.AutomaticAPI; +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.Random; +import java.util.Scanner; +import java.util.concurrent.ExecutionException; + +public class AutomaticExample { + + private static final String NETWORK_NAME = "Automatic"; + private static final String PROTECTED_RESOURCE_URL = "https://api.automatic.com/user/me/"; + + private AutomaticExample() { + } + + @SuppressWarnings("PMD.SystemPrintln") + public static void main(String... args) throws IOException, InterruptedException, ExecutionException { + // Replace these with your client id and secret + final String clientId = "your client id"; + final String clientSecret = "your client secret"; + final String secretState = "secret" + new Random().nextInt(999_999); + final OAuth20Service service = new ServiceBuilder(clientId) + .apiSecret(clientSecret) + .callback("http://www.example.com/oauth_callback/") + .defaultScope("scope:user:profile") + .build(AutomaticAPI.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(); + } + + // Trade the Authorization Code for the Access Token + 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! :)"); + } +} diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Box20Example.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Box20Example.java index de8a1c1ed..11673af20 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Box20Example.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Box20Example.java @@ -14,7 +14,7 @@ import java.util.Map; import java.util.concurrent.ExecutionException; -public final class Box20Example { +public class Box20Example { private static final String NETWORK_NAME = "Box"; private static final String PROTECTED_RESOURCE_URL = "https://api.box.com/2.0/users/me"; @@ -22,6 +22,7 @@ public final class Box20Example { private Box20Example() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { //Replace these with your client id and secret final String clientId = "your client id"; @@ -29,7 +30,6 @@ public static void main(String... args) throws IOException, InterruptedException final String secretState = "security_token" + new Random().nextInt(999_999); final OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) - .state(secretState) .callback("https://example.com/callback") .build(BoxApi20.instance()); final Scanner in = new Scanner(System.in, "UTF-8"); @@ -42,9 +42,12 @@ public static void main(String... args) throws IOException, InterruptedException //pass access_type=offline to get refresh token final Map additionalParams = new HashMap<>(); additionalParams.put("access_type", "offline"); - //force to reget refresh token (if usera are asked not the first time) + //force to reget refresh token (if user are asked not the first time) additionalParams.put("prompt", "consent"); - final String authorizationUrl = service.getAuthorizationUrl(additionalParams); + final String authorizationUrl = service.createAuthorizationUrlBuilder() + .state(secretState) + .additionalParams(additionalParams) + .build(); System.out.println("Got the Authorization URL!"); System.out.println("Now go and authorize ScribeJava here:"); System.out.println(authorizationUrl); @@ -65,12 +68,10 @@ public static void main(String... args) throws IOException, InterruptedException System.out.println(); } - // Trade the Request Token and Verfier for the Access Token - System.out.println("Trading the Request Token for an Access Token..."); + 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("(If you're curious, it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + System.out.println("(The raw response looks like this: " + accessToken.getRawResponse() + "')"); System.out.println(); @@ -78,12 +79,12 @@ 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); - final 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()); - + 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("Thats it man! Go and build something awesome with ScribeJava! :)"); } diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/DataportenExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/DataportenExample.java new file mode 100644 index 000000000..fe8fada2a --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/DataportenExample.java @@ -0,0 +1,80 @@ +package com.github.scribejava.apis.examples; + +import java.util.Random; +import java.util.Scanner; +import com.github.scribejava.apis.DataportenApi; +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.concurrent.ExecutionException; + +public class DataportenExample { + + private static final String NETWORK_NAME = "Dataporten"; + private static final String PROTECTED_RESOURCE_URL = "https://auth.dataporten.no/userinfo"; + + private DataportenExample() { + } + + @SuppressWarnings("PMD.SystemPrintln") + public static void main(String... args) throws IOException, InterruptedException, ExecutionException { + // Replace these with your client id and secret + final String clientId = "your client id"; + final String clientSecret = "your client secret"; + final String secretState = "secret" + new Random().nextInt(999_999); + final OAuth20Service service = new ServiceBuilder(clientId) + .apiSecret(clientSecret) + .callback("http://www.example.com/oauth_callback/") + .build(DataportenApi.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! :)"); + } +} diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/DiggExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/DiggExample.java index aafbea10c..eb2820683 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/DiggExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/DiggExample.java @@ -12,7 +12,7 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class DiggExample { +public class DiggExample { private static final String NETWORK_NAME = "Digg"; private static final String PROTECTED_RESOURCE_URL = "http://services.digg.com/2.0/comment.digg"; @@ -20,6 +20,7 @@ public final class DiggExample { private DiggExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your own api key and secret final String apiKey = "myKey"; @@ -49,12 +50,11 @@ public static void main(String... args) throws IOException, InterruptedException final String oauthVerifier = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token + // Trade the Request Token and Verifier for the Access Token System.out.println("Trading the Request Token for an Access Token..."); final OAuth1AccessToken accessToken = service.getAccessToken(requestToken, oauthVerifier); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + System.out.println("(The raw response looks like this: " + accessToken.getRawResponse() + "')"); System.out.println(); // Now let's go and ask for a protected resource! @@ -62,12 +62,12 @@ public static void main(String... args) throws IOException, InterruptedException final OAuthRequest request = new OAuthRequest(Verb.POST, PROTECTED_RESOURCE_URL); request.addBodyParameter("comment_id", "20100729223726:4fef610331ee46a3b5cbd740bf71313e"); service.signRequest(accessToken, request); - final 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()); - + 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! :)"); diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/DiscordExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/DiscordExample.java new file mode 100644 index 000000000..f42bde9ec --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/DiscordExample.java @@ -0,0 +1,90 @@ +package com.github.scribejava.apis.examples; + +import com.github.scribejava.apis.DiscordApi; +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.Random; +import java.util.Scanner; +import java.util.concurrent.ExecutionException; + +public class DiscordExample { + + private static final String NETWORK_NAME = "Discord"; + private static final String PROTECTED_RESOURCE_URL = "https://discordapp.com/api/users/@me"; + + private DiscordExample() { + } + + @SuppressWarnings("PMD.SystemPrintln") + public static void main(String... args) throws IOException, ExecutionException, InterruptedException { + // 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("identify") // replace with desired scope + .callback("http://example.com/callback") + .userAgent("ScribeJava") + .build(DiscordApi.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..."); + OAuth2AccessToken accessToken = service.getAccessToken(code); + System.out.println("Got the Access Token!"); + System.out.println("(if your curious it looks like this: " + accessToken + + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + + System.out.println("Refreshing the Access Token..."); + accessToken = service.refreshAccessToken(accessToken.getRefreshToken()); + System.out.println("Refreshed the Access Token!"); + System.out.println("(if your curious it looks like this: " + accessToken + + ", 'rawResponse'='" + 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! :)"); + } +} diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/DropboxExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/DropboxExample.java new file mode 100644 index 000000000..bcf02624a --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/DropboxExample.java @@ -0,0 +1,74 @@ +package com.github.scribejava.apis.examples; + +import com.github.scribejava.apis.DropboxApi; +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.Scanner; +import java.util.concurrent.ExecutionException; + +public class DropboxExample { + + private static final String NETWORK_NAME = "Dropbox.com"; + private static final String PROTECTED_RESOURCE_URL = "https://api.dropboxapi.com/2/users/get_space_usage"; + 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 DropboxExample() { + } + + @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/") + .build(DropboxApi.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 String authorizationUrl = service.getAuthorizationUrl(); + 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("Now we're going to access a protected resource..."); + final OAuthRequest request = new OAuthRequest(Verb.POST, PROTECTED_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("Thats it man! Go and build something awesome with ScribeJava! :)"); + } +} diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/EtsyExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/EtsyExample.java new file mode 100644 index 000000000..ad55cb07c --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/EtsyExample.java @@ -0,0 +1,66 @@ +package com.github.scribejava.apis.examples; + +import com.github.scribejava.apis.EtsyApi; +import com.github.scribejava.core.builder.ServiceBuilder; +import com.github.scribejava.core.model.OAuth1AccessToken; +import com.github.scribejava.core.model.OAuth1RequestToken; +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.OAuth10aService; + +import java.io.IOException; +import java.util.Scanner; +import java.util.concurrent.ExecutionException; + +public class EtsyExample { + + private static final String PROTECTED_RESOURCE_URL = "https://openapi.etsy.com/v2/users/__SELF__"; + + private EtsyExample() { + } + + @SuppressWarnings("PMD.SystemPrintln") + public static void main(String[] args) throws InterruptedException, ExecutionException, IOException { + // Replace with your api and secret key + final OAuth10aService service = new ServiceBuilder("your api key") + .apiSecret("your secret key") + .build(EtsyApi.instance()); + final Scanner in = new Scanner(System.in); + + System.out.println("=== Etsy's OAuth Workflow ==="); + System.out.println(); + + // Obtain the Request Token + System.out.println("Fetching the Request Token..."); + final OAuth1RequestToken requestToken = service.getRequestToken(); + System.out.println("Got the Request Token!"); + System.out.println(); + + System.out.println("Now go and authorize ScribeJava here:"); + System.out.println(service.getAuthorizationUrl(requestToken)); + System.out.println("And paste the verifier here"); + System.out.print(">>"); + final String oauthVerifier = in.nextLine(); + System.out.println(); + + // Trade the Request Token and Verifier for the Access Token + System.out.println("Trading the Request Token for an Access Token..."); + final OAuth1AccessToken accessToken = service.getAccessToken(requestToken, oauthVerifier); + 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.getBody()); + } + System.out.println(); + System.out.println("That's it man! Go and build something awesome with ScribeJava! :)"); + } +} diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FacebookExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FacebookExample.java index 521020e29..db8f6bdea 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FacebookExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FacebookExample.java @@ -12,14 +12,15 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class FacebookExample { +public class FacebookExample { private static final String NETWORK_NAME = "Facebook"; - private static final String PROTECTED_RESOURCE_URL = "https://graph.facebook.com/v2.8/me"; + private static final String PROTECTED_RESOURCE_URL = "https://graph.facebook.com/v3.2/me"; private FacebookExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your client id and secret final String clientId = "your client id"; @@ -27,7 +28,6 @@ public static void main(String... args) throws IOException, InterruptedException final String secretState = "secret" + new Random().nextInt(999_999); final OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) - .state(secretState) .callback("http://www.example.com/oauth_callback/") .build(FacebookApi.instance()); @@ -38,7 +38,7 @@ public static void main(String... args) throws IOException, InterruptedException // Obtain the Authorization URL System.out.println("Fetching the Authorization URL..."); - final String authorizationUrl = service.getAuthorizationUrl(); + 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); @@ -59,24 +59,22 @@ public static void main(String... args) throws IOException, InterruptedException System.out.println(); } - // Trade the Request Token and Verfier for the Access Token - System.out.println("Trading the Request Token for an Access Token..."); + 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("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final 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()); - + 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! :)"); diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FitbitApi20Example.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FitbitApi20Example.java new file mode 100644 index 000000000..e5818a92a --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FitbitApi20Example.java @@ -0,0 +1,81 @@ +package com.github.scribejava.apis.examples; + +import java.util.Scanner; + +import com.github.scribejava.apis.FitbitApi20; +import com.github.scribejava.apis.fitbit.FitBitOAuth2AccessToken; +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 FitbitApi20Example { + + private static final String NETWORK_NAME = "Fitbit"; + + private static final String PROTECTED_RESOURCE_URL = "https://api.fitbit.com/1/user/%s/profile.json"; + + private FitbitApi20Example() { + } + + @SuppressWarnings("PMD.SystemPrintln") + public static void main(String... args) throws Exception { + + // Replace these with your client id and secret fron your app + final String clientId = "your client id"; + final String clientSecret = "your client secret"; + final OAuth20Service service = new ServiceBuilder(clientId) + .apiSecret(clientSecret) + .defaultScope("activity profile") // replace with desired scope + //your callback URL to store and handle the authorization code sent by Fitbit + .callback("http://www.example.com/oauth_callback/") + .build(FitbitApi20.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 String authorizationUrl = service.getAuthorizationUrl("some_params"); + 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 oauth2AccessToken = service.getAccessToken(code); + System.out.println("Got the Access Token!"); + System.out.println("(if your curious it looks like this: " + oauth2AccessToken + + ", 'rawResponse'='" + oauth2AccessToken.getRawResponse() + "')"); + System.out.println(); + + if (!(oauth2AccessToken instanceof FitBitOAuth2AccessToken)) { + System.out.println("oauth2AccessToken is not instance of FitBitOAuth2AccessToken. Strange enough. exit."); + return; + } + + final FitBitOAuth2AccessToken accessToken = (FitBitOAuth2AccessToken) oauth2AccessToken; + // Now let's go and ask for a protected resource! + // This will get the profile for this user + System.out.println("Now we're going to access a protected resource..."); + + final OAuthRequest request = new OAuthRequest(Verb.GET, + String.format(PROTECTED_RESOURCE_URL, accessToken.getUserId())); + request.addHeader("x-li-format", "json"); + + service.signRequest(accessToken, request); + + System.out.println(); + try (Response response = service.execute(request)) { + System.out.println(response.getCode()); + System.out.println(response.getBody()); + } + System.out.println(); + } +} diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FlickrExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FlickrExample.java index e5809874f..8cdf8a209 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FlickrExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FlickrExample.java @@ -13,13 +13,14 @@ import java.util.Scanner; import java.util.concurrent.ExecutionException; -public final class FlickrExample { +public class FlickrExample { private static final String PROTECTED_RESOURCE_URL = "http://api.flickr.com/services/rest/"; private FlickrExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your own api key and secret final String apiKey = "your_app_id"; @@ -47,12 +48,11 @@ public static void main(String... args) throws IOException, InterruptedException final String oauthVerifier = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token + // Trade the Request Token and Verifier for the Access Token System.out.println("Trading the Request Token for an Access Token..."); final OAuth1AccessToken accessToken = service.getAccessToken(requestToken, oauthVerifier); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + System.out.println("(The raw response looks like this: " + accessToken.getRawResponse() + "')"); System.out.println(); // Now let's go and ask for a protected resource! @@ -60,11 +60,11 @@ public static void main(String... args) throws IOException, InterruptedException final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); request.addQuerystringParameter("method", "flickr.test.login"); service.signRequest(accessToken, request); - final Response response = service.execute(request); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getBody()); - + try (Response response = service.execute(request)) { + System.out.println("Got it! Lets see what we found..."); + System.out.println(); + 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/Foursquare2Example.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Foursquare2Example.java index 33e6206ef..cdd29180a 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Foursquare2Example.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Foursquare2Example.java @@ -11,7 +11,7 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class Foursquare2Example { +public class Foursquare2Example { private static final String PROTECTED_RESOURCE_URL = "https://api.foursquare.com/v2/users/self/friends?oauth_token="; @@ -19,6 +19,7 @@ public final class Foursquare2Example { private Foursquare2Example() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your own api key and secret final String apiKey = "your client id"; @@ -43,24 +44,22 @@ public static void main(String... args) throws IOException, InterruptedException final String code = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token - System.out.println("Trading the Request Token for an Access Token..."); + 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("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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 + accessToken.getAccessToken()); service.signRequest(accessToken, request); - final 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()); - + 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! :)"); diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FoursquareExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FoursquareExample.java index afaaca188..6aa3c9435 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FoursquareExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FoursquareExample.java @@ -12,13 +12,14 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class FoursquareExample { +public class FoursquareExample { private static final String PROTECTED_RESOURCE_URL = "http://api.foursquare.com/v1/user"; private FoursquareExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { final OAuth10aService service = new ServiceBuilder("your client id") .apiSecret("your client secret") @@ -41,23 +42,22 @@ public static void main(String... args) throws IOException, InterruptedException final String oauthVerifier = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token + // Trade the Request Token and Verifier for the Access Token System.out.println("Trading the Request Token for an Access Token..."); final OAuth1AccessToken accessToken = service.getAccessToken(requestToken, oauthVerifier); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final Response response = service.execute(request); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getBody()); - + try (Response response = service.execute(request)) { + System.out.println("Got it! Lets see what we found..."); + System.out.println(); + 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/FrappeExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FrappeExample.java index fe704ed7a..827f8aacd 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FrappeExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FrappeExample.java @@ -11,7 +11,7 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class FrappeExample { +public class FrappeExample { private static final String NETWORK_NAME = "Frappe"; private static final String PROTECTED_RESOURCE_PATH = "/api/method/frappe.integrations.oauth2.openid_profile"; @@ -19,6 +19,7 @@ public final class FrappeExample { private FrappeExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { //Replace these with your client id and secret final String clientId = "clientId"; @@ -26,7 +27,7 @@ public static void main(String... args) throws IOException, InterruptedException final String clientDomain = "https://example.com"; final OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) - .scope("openid all") + .defaultScope("openid all") .callback("https://example.com/callback") .build(FrappeApi.instance(clientDomain)); final Scanner in = new Scanner(System.in, "UTF-8"); @@ -49,21 +50,19 @@ public static void main(String... args) throws IOException, InterruptedException System.out.println("Trading the Request Token for an Access Token..."); final OAuth2AccessToken accessToken = service.getAccessToken(code); System.out.println("Got the Access Token!"); - System.out.println("(If you're curious, it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); - + 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, clientDomain + PROTECTED_RESOURCE_PATH); service.signRequest(accessToken, request); - final 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()); - + 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("Thats it man! Go and build something awesome with ScribeJava! :)"); } } diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FreelancerExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FreelancerExample.java index a17f896de..023d789c4 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FreelancerExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FreelancerExample.java @@ -12,7 +12,7 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class FreelancerExample { +public class FreelancerExample { private static final String NETWORK_NAME = "Freelancer"; private static final String AUTHORIZE_URL @@ -23,10 +23,11 @@ public final class FreelancerExample { private FreelancerExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { final OAuth10aService service = new ServiceBuilder("your client id") .apiSecret("your client secret") - .scope(SCOPE) + .withScope(SCOPE) .build(FreelancerApi.Sandbox.instance()); final Scanner in = new Scanner(System.in); @@ -37,7 +38,7 @@ public static void main(String... args) throws IOException, InterruptedException System.out.println("Fetching the Request Token..."); final OAuth1RequestToken requestToken = service.getRequestToken(); System.out.println("Got the Request Token!"); - System.out.println("(if your curious it looks like this: " + requestToken + " )"); + System.out.println("(The raw response looks like this: " + requestToken.getRawResponse() + "')"); System.out.println(); System.out.println("Now go and authorize ScribeJava here:"); @@ -47,12 +48,11 @@ public static void main(String... args) throws IOException, InterruptedException final String oauthVerifier = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token + // Trade the Request Token and Verifier for the Access Token System.out.println("Trading the Request Token for an Access Token..."); final OAuth1AccessToken accessToken = service.getAccessToken(requestToken, oauthVerifier); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + System.out.println("(The raw response looks like this: " + accessToken.getRawResponse() + "')"); System.out.println(); // Now let's go and ask for a protected resource! @@ -60,12 +60,12 @@ public static void main(String... args) throws IOException, InterruptedException final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); request.addHeader("GData-Version", "3.0"); - final 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()); - + 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! :)"); } diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/GeniusExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/GeniusExample.java index af515b79c..9749a168e 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/GeniusExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/GeniusExample.java @@ -12,7 +12,7 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class GeniusExample { +public class GeniusExample { private static final String NETWORK_NAME = "Genius"; private static final String PROTECTED_RESOURCE_URL = "https://api.genius.com/songs/378195"; @@ -20,6 +20,7 @@ public final class GeniusExample { private GeniusExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your client id and secret final String clientId = "your client id"; @@ -27,8 +28,7 @@ public static void main(String... args) throws IOException, InterruptedException final String secretState = "100"; final OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) - .scope("me") - .state(secretState) + .defaultScope("me") .callback("com.scribejavatest://callback") .userAgent("ScribeJava") .build(GeniusApi.instance()); @@ -38,7 +38,7 @@ public static void main(String... args) throws IOException, InterruptedException // Obtain the Authorization URL System.out.println("Fetching the Authorization URL..."); - final String authorizationUrl = service.getAuthorizationUrl(); + 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); @@ -77,12 +77,12 @@ public static void main(String... args) throws IOException, InterruptedException System.out.println("Accessing a protected resource..."); final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = service.execute(request); - System.out.println("Got it! Viewing contents..."); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); - + try (Response response = service.execute(request)) { + System.out.println("Got it! Viewing contents..."); + 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/GitHubAsyncOkHttpExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/GitHubAsyncOkHttpExample.java index 596a14a45..a0c94792c 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/GitHubAsyncOkHttpExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/GitHubAsyncOkHttpExample.java @@ -14,7 +14,7 @@ import java.util.Scanner; import java.util.concurrent.ExecutionException; -public final class GitHubAsyncOkHttpExample { +public class GitHubAsyncOkHttpExample { private static final String NETWORK_NAME = "GitHub"; private static final String PROTECTED_RESOURCE_URL = "https://api.github.com/user"; @@ -22,6 +22,7 @@ public final class GitHubAsyncOkHttpExample { private GitHubAsyncOkHttpExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, ExecutionException, InterruptedException { // Replace these with your client id and secret final String clientId = "your client id"; @@ -29,7 +30,6 @@ public static void main(String... args) throws IOException, ExecutionException, final String secretState = "secret" + new Random().nextInt(999_999); try (OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) - .state(secretState) .callback("http://www.example.com/oauth_callback/") .httpClientConfig(OkHttpHttpClientConfig.defaultConfig()) .build(GitHubApi.instance())) { @@ -40,7 +40,7 @@ public static void main(String... args) throws IOException, ExecutionException, // Obtain the Authorization URL System.out.println("Fetching the Authorization URL..."); - final String authorizationUrl = service.getAuthorizationUrl(); + 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); @@ -62,24 +62,23 @@ public static void main(String... args) throws IOException, ExecutionException, System.out.println(); } - // Trade the Request Token and Verfier for the Access Token - System.out.println("Trading the Request Token for an Access Token..."); + 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("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final 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()); - + 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! :)"); } diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/GitHubExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/GitHubExample.java index 8c3444d92..bfd3f2512 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/GitHubExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/GitHubExample.java @@ -12,7 +12,7 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class GitHubExample { +public class GitHubExample { private static final String NETWORK_NAME = "GitHub"; private static final String PROTECTED_RESOURCE_URL = "https://api.github.com/user"; @@ -20,6 +20,7 @@ public final class GitHubExample { private GitHubExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your client id and secret final String clientId = "your client id"; @@ -27,7 +28,6 @@ public static void main(String... args) throws IOException, InterruptedException final String secretState = "secret" + new Random().nextInt(999_999); final OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) - .state(secretState) .callback("http://www.example.com/oauth_callback/") .build(GitHubApi.instance()); final Scanner in = new Scanner(System.in, "UTF-8"); @@ -37,7 +37,7 @@ public static void main(String... args) throws IOException, InterruptedException // Obtain the Authorization URL System.out.println("Fetching the Authorization URL..."); - final String authorizationUrl = service.getAuthorizationUrl(); + 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); @@ -58,24 +58,22 @@ public static void main(String... args) throws IOException, InterruptedException System.out.println(); } - // Trade the Request Token and Verfier for the Access Token - System.out.println("Trading the Request Token for an Access Token..."); + 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("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final 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()); - + 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! :)"); } diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20ArmeriaExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20ArmeriaExample.java new file mode 100644 index 000000000..04fddb1da --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20ArmeriaExample.java @@ -0,0 +1,118 @@ +package com.github.scribejava.apis.examples; + +import java.util.Random; +import java.util.Scanner; +import com.github.scribejava.apis.GoogleApi20; +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 com.github.scribejava.httpclient.armeria.ArmeriaHttpClientConfig; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +public class Google20ArmeriaExample { + + private static final String NETWORK_NAME = "Google Armeria"; + private static final String PROTECTED_RESOURCE_URL = "https://www.googleapis.com/oauth2/v3/userinfo"; + + private Google20ArmeriaExample() { + } + + @SuppressWarnings("PMD.SystemPrintln") + public static void main(String... args) throws InterruptedException, ExecutionException, IOException { + // Replace these with your client id and secret + final String clientId = "your client id"; + final String clientSecret = "your client secret"; + final String secretState = "secret" + new Random().nextInt(999_999); + + try (OAuth20Service service = new ServiceBuilder(clientId) + .apiSecret(clientSecret) + .defaultScope("profile") // replace with desired scope + .callback("http://example.com/callback") + .httpClientConfig(ArmeriaHttpClientConfig.defaultConfig()) + .build(GoogleApi20.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..."); + //pass access_type=offline to get refresh token + //https://developers.google.com/identity/protocols/OAuth2WebServer#preparing-to-start-the-oauth-20-flow + final Map additionalParams = new HashMap<>(); + additionalParams.put("access_type", "offline"); + //force to reget refresh token (if user are asked not the first time) + additionalParams.put("prompt", "consent"); + final String authorizationUrl = service.createAuthorizationUrlBuilder() + .state(secretState) + .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("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..."); + 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("Refreshing the Access Token..."); + accessToken = service.refreshAccessToken(accessToken.getRefreshToken()); + System.out.println("Refreshed 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..."); + while (true) { + System.out.println("Paste fieldnames to fetch (leave empty to get profile, 'exit' to stop example)"); + System.out.print(">>"); + final String query = in.nextLine(); + System.out.println(); + + final String requestUrl; + if ("exit".equals(query)) { + break; + } else if (query == null || query.isEmpty()) { + requestUrl = PROTECTED_RESOURCE_URL; + } else { + requestUrl = PROTECTED_RESOURCE_URL + "?fields=" + query; + } + + final OAuthRequest request = new OAuthRequest(Verb.GET, requestUrl); + service.signRequest(accessToken, request); + System.out.println(); + try (Response response = service.execute(request)) { + System.out.println(response.getCode()); + System.out.println(response.getBody()); + } + System.out.println(); + } + } + } +} diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20AsyncAHCExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20AsyncAHCExample.java index 7ac82e7e9..a894db2bf 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20AsyncAHCExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20AsyncAHCExample.java @@ -17,14 +17,15 @@ import java.util.concurrent.ExecutionException; import org.asynchttpclient.DefaultAsyncHttpClientConfig; -public final class Google20AsyncAHCExample { +public class Google20AsyncAHCExample { - private static final String NETWORK_NAME = "G+ Async"; - private static final String PROTECTED_RESOURCE_URL = "https://www.googleapis.com/plus/v1/people/me"; + private static final String NETWORK_NAME = "Google Async"; + private static final String PROTECTED_RESOURCE_URL = "https://www.googleapis.com/oauth2/v3/userinfo"; private Google20AsyncAHCExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws InterruptedException, ExecutionException, IOException { // Replace these with your client id and secret final String clientId = "your client id"; @@ -39,8 +40,7 @@ public static void main(String... args) throws InterruptedException, ExecutionEx try (OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) - .scope("profile") // replace with desired scope - .state(secretState) + .defaultScope("profile") // replace with desired scope .callback("http://example.com/callback") .httpClientConfig(clientConfig) .build(GoogleApi20.instance())) { @@ -55,9 +55,12 @@ public static void main(String... args) throws InterruptedException, ExecutionEx //https://developers.google.com/identity/protocols/OAuth2WebServer#preparing-to-start-the-oauth-20-flow final Map additionalParams = new HashMap<>(); additionalParams.put("access_type", "offline"); - //force to reget refresh token (if usera are asked not the first time) + //force to reget refresh token (if user are asked not the first time) additionalParams.put("prompt", "consent"); - final String authorizationUrl = service.getAuthorizationUrl(additionalParams); + final String authorizationUrl = service.createAuthorizationUrlBuilder() + .state(secretState) + .additionalParams(additionalParams) + .build(); System.out.println("Got the Authorization URL!"); System.out.println("Now go and authorize ScribeJava here:"); System.out.println(authorizationUrl); @@ -79,18 +82,17 @@ public static void main(String... args) throws InterruptedException, ExecutionEx System.out.println(); } - // Trade the Request Token and Verfier for the Access Token - System.out.println("Trading the Request Token for an Access Token..."); + System.out.println("Trading the Authorization Code for an Access Token..."); OAuth2AccessToken accessToken = service.getAccessToken(code); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + System.out.println("(The raw response looks like this: " + accessToken.getRawResponse() + + "')"); System.out.println("Refreshing the Access Token..."); accessToken = service.refreshAccessToken(accessToken.getRefreshToken()); System.out.println("Refreshed the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + System.out.println("(The raw response looks like this: " + accessToken.getRawResponse() + + "')"); System.out.println(); // Now let's go and ask for a protected resource! @@ -112,11 +114,11 @@ public static void main(String... args) throws InterruptedException, ExecutionEx final OAuthRequest request = new OAuthRequest(Verb.GET, requestUrl); service.signRequest(accessToken, request); - final Response response = service.execute(request); System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); - + try (Response response = service.execute(request)) { + System.out.println(response.getCode()); + System.out.println(response.getBody()); + } System.out.println(); } } diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20DeviceAuthorizationGrantExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20DeviceAuthorizationGrantExample.java new file mode 100644 index 000000000..fc361e13f --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20DeviceAuthorizationGrantExample.java @@ -0,0 +1,82 @@ +package com.github.scribejava.apis.examples; + +import com.github.scribejava.core.builder.ServiceBuilder; +import com.github.scribejava.apis.GoogleApi20; +import com.github.scribejava.core.model.DeviceAuthorization; +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.Scanner; +import java.util.concurrent.ExecutionException; + +public class Google20DeviceAuthorizationGrantExample { + + private static final String NETWORK_NAME = "Google"; + private static final String PROTECTED_RESOURCE_URL = "https://www.googleapis.com/oauth2/v3/userinfo"; + + private Google20DeviceAuthorizationGrantExample() { + } + + @SuppressWarnings("PMD.SystemPrintln") + public static void main(String... args) throws IOException, InterruptedException, ExecutionException { + // Replace these with your client id and secret + final String clientId = "your client id"; + final String clientSecret = "your_client_secret"; + + final OAuth20Service service = new ServiceBuilder(clientId) + .debug() + .apiSecret(clientSecret) + .defaultScope("profile") // replace with desired scope + .build(GoogleApi20.instance()); + final Scanner in = new Scanner(System.in, "UTF-8"); + + System.out.println("=== " + NETWORK_NAME + "'s OAuth Workflow ==="); + System.out.println(); + + System.out.println("Requesting a set of verification codes..."); + + final DeviceAuthorization deviceAuthorization = service.getDeviceAuthorizationCodes(); + System.out.println("Got the Device Authorization Codes!"); + System.out.println(deviceAuthorization); + + System.out.println("Now go and authorize ScribeJava. Visit: " + deviceAuthorization.getVerificationUri() + + " and enter the code: " + deviceAuthorization.getUserCode()); + if (deviceAuthorization.getVerificationUriComplete() != null) { + System.out.println("Or visit " + deviceAuthorization.getVerificationUriComplete()); + } + + System.out.println("Polling for an Access Token..."); + final OAuth2AccessToken accessToken = service.pollAccessTokenDeviceAuthorizationGrant(deviceAuthorization); + + System.out.println("Got the Access Token!"); + System.out.println("(The raw response looks like this: " + accessToken.getRawResponse() + "')"); + + // Now let's go and ask for a protected resource! + System.out.println("Now we're going to access a protected resource..."); + while (true) { + System.out.println("Paste fieldnames to fetch (leave empty to get profile, 'exit' to stop the example)"); + System.out.print(">>"); + final String query = in.nextLine(); + System.out.println(); + final String requestUrl; + if ("exit".equals(query)) { + break; + } else if (query == null || query.isEmpty()) { + requestUrl = PROTECTED_RESOURCE_URL; + } else { + requestUrl = PROTECTED_RESOURCE_URL + "?fields=" + query; + } + final OAuthRequest request = new OAuthRequest(Verb.GET, requestUrl); + service.signRequest(accessToken, request); + System.out.println(); + try (Response response = service.execute(request)) { + System.out.println(response.getCode()); + System.out.println(response.getBody()); + } + System.out.println(); + } + } +} diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20Example.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20Example.java index e9e2adf27..ca70409f4 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20Example.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20Example.java @@ -14,23 +14,23 @@ import java.util.Map; import java.util.concurrent.ExecutionException; -public final class Google20Example { +public class Google20Example { - private static final String NETWORK_NAME = "G+"; - private static final String PROTECTED_RESOURCE_URL = "https://www.googleapis.com/plus/v1/people/me"; + private static final String NETWORK_NAME = "Google"; + private static final String PROTECTED_RESOURCE_URL = "https://www.googleapis.com/oauth2/v3/userinfo"; private Google20Example() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your client id and secret - final String clientId = "your client id"; - final String clientSecret = "your client secret"; + final String clientId = "your_client_id"; + final String clientSecret = "your_client_secret"; final String secretState = "secret" + new Random().nextInt(999_999); final OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) - .scope("profile") // replace with desired scope - .state(secretState) + .defaultScope("profile") // replace with desired scope .callback("http://example.com/callback") .build(GoogleApi20.instance()); final Scanner in = new Scanner(System.in, "UTF-8"); @@ -44,9 +44,12 @@ public static void main(String... args) throws IOException, InterruptedException //https://developers.google.com/identity/protocols/OAuth2WebServer#preparing-to-start-the-oauth-20-flow final Map additionalParams = new HashMap<>(); additionalParams.put("access_type", "offline"); - //force to reget refresh token (if usera are asked not the first time) + //force to reget refresh token (if user are asked not the first time) additionalParams.put("prompt", "consent"); - final String authorizationUrl = service.getAuthorizationUrl(additionalParams); + final String authorizationUrl = service.createAuthorizationUrlBuilder() + .state(secretState) + .additionalParams(additionalParams) + .build(); System.out.println("Got the Authorization URL!"); System.out.println("Now go and authorize ScribeJava here:"); System.out.println(authorizationUrl); @@ -67,18 +70,15 @@ public static void main(String... args) throws IOException, InterruptedException System.out.println(); } - // Trade the Request Token and Verfier for the Access Token - System.out.println("Trading the Request Token for an Access Token..."); + System.out.println("Trading the Authorization Code for an Access Token..."); OAuth2AccessToken accessToken = service.getAccessToken(code); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + System.out.println("(The raw response looks like this: " + accessToken.getRawResponse() + "')"); System.out.println("Refreshing the Access Token..."); accessToken = service.refreshAccessToken(accessToken.getRefreshToken()); System.out.println("Refreshed the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + System.out.println("(The raw response looks like this: " + accessToken.getRawResponse() + "')"); System.out.println(); // Now let's go and ask for a protected resource! @@ -100,11 +100,11 @@ public static void main(String... args) throws IOException, InterruptedException final OAuthRequest request = new OAuthRequest(Verb.GET, requestUrl); service.signRequest(accessToken, request); - final Response response = service.execute(request); System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); - + try (Response response = service.execute(request)) { + System.out.println(response.getCode()); + System.out.println(response.getBody()); + } System.out.println(); } } diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20RevokeExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20RevokeExample.java new file mode 100644 index 000000000..ba513ca52 --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20RevokeExample.java @@ -0,0 +1,109 @@ +package com.github.scribejava.apis.examples; + +import java.util.Random; +import java.util.Scanner; +import com.github.scribejava.core.builder.ServiceBuilder; +import com.github.scribejava.apis.GoogleApi20; +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.concurrent.ExecutionException; + +public class Google20RevokeExample { + + private static final String NETWORK_NAME = "Google"; + private static final String PROTECTED_RESOURCE_URL = "https://www.googleapis.com/oauth2/v3/userinfo"; + + private Google20RevokeExample() { + } + + @SuppressWarnings("PMD.SystemPrintln") + public static void main(String... args) throws IOException, InterruptedException, ExecutionException { + // Replace these with your client id and secret + final String clientId = "your client id"; + final String clientSecret = "your client secret"; + final String secretState = "secret" + new Random().nextInt(999_999); + final OAuth20Service service = new ServiceBuilder(clientId) + .apiSecret(clientSecret) + .defaultScope("profile") // replace with desired scope + .callback("http://example.com/callback") + .build(GoogleApi20.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..."); + //pass access_type=offline to get refresh token + //https://developers.google.com/identity/protocols/OAuth2WebServer#preparing-to-start-the-oauth-20-flow + final Map additionalParams = new HashMap<>(); + additionalParams.put("access_type", "offline"); + //force to reget refresh token (if user are asked not the first time) + additionalParams.put("prompt", "consent"); + final String authorizationUrl = service.createAuthorizationUrlBuilder() + .state(secretState) + .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("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() + "')"); + + // Now let's go and ask for a protected resource! + System.out.println("Now we're going to access a protected resource..."); + OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); + service.signRequest(accessToken, request); + System.out.println(); + try (Response response = service.execute(request)) { + System.out.println(response.getCode()); + System.out.println(response.getBody()); + } + System.out.println(); + System.out.println("Revoking token..."); + service.revokeToken(accessToken.getAccessToken()); + System.out.println("done."); + System.out.println("After revoke we should fail requesting any data... Press enter to try"); + in.nextLine(); + //Google Note: Following a successful revocation response, + //it might take some time before the revocation has full effect. + int responseCode; + do { + Thread.sleep(1000); + request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); + service.signRequest(accessToken, request); + System.out.println(); + try (Response response = service.execute(request)) { + responseCode = response.getCode(); + System.out.println(responseCode); + System.out.println(response.getBody()); + } + System.out.println(); + } while (responseCode == 200); + } +} diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20WithPKCEExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20WithPKCEExample.java new file mode 100644 index 000000000..8c840b8d5 --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Google20WithPKCEExample.java @@ -0,0 +1,117 @@ +package com.github.scribejava.apis.examples; + +import java.util.Random; +import java.util.Scanner; +import com.github.scribejava.core.builder.ServiceBuilder; +import com.github.scribejava.apis.GoogleApi20; +import com.github.scribejava.core.oauth.AuthorizationUrlBuilder; +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.AccessTokenRequestParams; +import com.github.scribejava.core.oauth.OAuth20Service; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +public class Google20WithPKCEExample { + + private static final String NETWORK_NAME = "Google"; + private static final String PROTECTED_RESOURCE_URL = "https://www.googleapis.com/oauth2/v3/userinfo"; + + private Google20WithPKCEExample() { + } + + @SuppressWarnings("PMD.SystemPrintln") + public static void main(String... args) throws IOException, InterruptedException, ExecutionException { + // Replace these with your client id and secret + final String clientId = "your client id"; + final String clientSecret = "your client secret"; + final String secretState = "secret" + new Random().nextInt(999_999); + final OAuth20Service service = new ServiceBuilder(clientId) + .apiSecret(clientSecret) + .defaultScope("profile") // replace with desired scope + .callback("http://example.com/callback") + .build(GoogleApi20.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..."); + //pass access_type=offline to get refresh token + //https://developers.google.com/identity/protocols/OAuth2WebServer#preparing-to-start-the-oauth-20-flow + final Map additionalParams = new HashMap<>(); + additionalParams.put("access_type", "offline"); + //force to reget refresh token (if user are asked not the first time) + additionalParams.put("prompt", "consent"); + + final AuthorizationUrlBuilder authorizationUrlBuilder = service.createAuthorizationUrlBuilder() + .state(secretState) + .additionalParams(additionalParams) + .initPKCE(); + + System.out.println("Got the Authorization URL!"); + System.out.println("Now go and authorize ScribeJava here:"); + System.out.println(authorizationUrlBuilder.build()); + 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..."); + OAuth2AccessToken accessToken = service.getAccessToken(AccessTokenRequestParams.create(code) + .pkceCodeVerifier(authorizationUrlBuilder.getPkce().getCodeVerifier())); + System.out.println("Got the Access Token!"); + System.out.println("(The raw response looks like this: " + accessToken.getRawResponse() + "')"); + + System.out.println("Refreshing the Access Token..."); + accessToken = service.refreshAccessToken(accessToken.getRefreshToken()); + System.out.println("Refreshed 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..."); + while (true) { + System.out.println("Paste fieldnames to fetch (leave empty to get profile, 'exit' to stop example)"); + System.out.print(">>"); + final String query = in.nextLine(); + System.out.println(); + + final String requestUrl; + if ("exit".equals(query)) { + break; + } else if (query == null || query.isEmpty()) { + requestUrl = PROTECTED_RESOURCE_URL; + } else { + requestUrl = PROTECTED_RESOURCE_URL + "?fields=" + query; + } + + final OAuthRequest request = new OAuthRequest(Verb.GET, requestUrl); + service.signRequest(accessToken, request); + System.out.println(); + try (Response response = service.execute(request)) { + System.out.println(response.getCode()); + System.out.println(response.getBody()); + } + System.out.println(); + } + } +} diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/HHExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/HHExample.java index e892eace3..c013b7d2b 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/HHExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/HHExample.java @@ -13,7 +13,7 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class HHExample { +public class HHExample { private static final String NETWORK_NAME = "hh.ru"; private static final String PROTECTED_RESOURCE_URL = "https://api.hh.ru/me"; @@ -21,6 +21,7 @@ public final class HHExample { private HHExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your own client id and secret final String clientId = "your client id"; @@ -45,23 +46,21 @@ public static void main(String... args) throws IOException, InterruptedException final String code = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token - System.out.println("Trading the Request Token for an Access Token..."); + 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("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + System.out.println("(The raw response looks like this: " + accessToken.getRawResponse() + "')"); System.out.println(); 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); - final 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()); - + 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! :)"); } diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/HiOrgServerExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/HiOrgServerExample.java new file mode 100644 index 000000000..2602d1257 --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/HiOrgServerExample.java @@ -0,0 +1,92 @@ +package com.github.scribejava.apis.examples; + +import com.github.scribejava.apis.HiOrgServerApi20; +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.Random; +import java.util.Scanner; +import java.util.concurrent.ExecutionException; + +public class HiOrgServerExample { + + private static final String NETWORK_NAME = "HiOrgServer"; + private static final String PROTECTED_RESOURCE_URL = "https://www.hiorg-server.de/api/oauth2/v1/user.php"; + private static final String CLIENT_ID = "your client id"; + private static final String CLIENT_SECRET = "your client secret"; + private static final String CALLBACK_URL = "http://www.example.com/oauth_callback/"; + + private HiOrgServerExample() { + } + + @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("basic eigenedaten") + .callback(CALLBACK_URL) + .build(HiOrgServerApi20.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..."); + 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("Refreshing the Access Token..."); + accessToken = service.refreshAccessToken(accessToken.getRefreshToken()); + System.out.println("Refreshed 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! :)"); + } + +} diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/ImgurExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/ImgurExample.java index 35f6d9a31..229e3a7ee 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/ImgurExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/ImgurExample.java @@ -12,7 +12,7 @@ import java.util.Scanner; import java.util.concurrent.ExecutionException; -public final class ImgurExample { +public class ImgurExample { private static final String NETWORK_NAME = "Imgur"; private static final String PROTECTED_RESOURCE_URL = "https://api.imgur.com/3/account/me"; @@ -20,6 +20,7 @@ public final class ImgurExample { private ImgurExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your own api key and secret final String apiKey = "your client id"; @@ -47,20 +48,19 @@ public static void main(String... args) throws IOException, InterruptedException System.out.println("Trading the Request Token for an Access Token..."); final OAuth2AccessToken accessToken = service.getAccessToken(code); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final 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()); - + 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! :)"); } 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/Kaixin20Example.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Kaixin20Example.java index 6d61a613b..482453cb4 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Kaixin20Example.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Kaixin20Example.java @@ -11,7 +11,7 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class Kaixin20Example { +public class Kaixin20Example { private static final String NETWORK_NAME = "Kaixin"; private static final String PROTECTED_RESOURCE_URL = "https://api.kaixin001.com/users/me.json"; @@ -19,6 +19,7 @@ public final class Kaixin20Example { private Kaixin20Example() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your own api key and secret final String apiKey = "your api key"; @@ -47,20 +48,19 @@ public static void main(String... args) throws IOException, InterruptedException System.out.println("Trading the Request Token for an Access Token..."); final OAuth2AccessToken accessToken = service.getAccessToken(code); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final 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()); - + 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! :)"); diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/KakaoExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/KakaoExample.java new file mode 100644 index 000000000..643182ea8 --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/KakaoExample.java @@ -0,0 +1,70 @@ +package com.github.scribejava.apis.examples; + +import com.github.scribejava.apis.KakaoApi; +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.Scanner; +import java.util.concurrent.ExecutionException; + +public class KakaoExample { + + private static final String NETWORK_NAME = "Kakao"; + private static final String PROTECTED_RESOURCE_URL = "https://kapi.kakao.com/v2/user/me"; + + private KakaoExample() { + } + + @SuppressWarnings("PMD.SystemPrintln") + public static void main(String... args) throws IOException, InterruptedException, ExecutionException { + // Replace this with your client id + final String clientId = "your client id"; + + final OAuth20Service service = new ServiceBuilder(clientId) + .callback("http://www.example.com/oauth_callback/") + .build(KakaoApi.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(); + 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(); + + // 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! :)"); + + } +} diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/KeycloakExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/KeycloakExample.java new file mode 100644 index 000000000..2d78ffb97 --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/KeycloakExample.java @@ -0,0 +1,72 @@ +package com.github.scribejava.apis.examples; + +import com.github.scribejava.apis.KeycloakApi; +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.Scanner; +import java.util.concurrent.ExecutionException; + +public class KeycloakExample { + + private KeycloakExample() { + } + + @SuppressWarnings("PMD.SystemPrintln") + public static void main(String... args) throws IOException, InterruptedException, ExecutionException { + // Replace these with your own api key, secret, callback, base url and realm + final String apiKey = "your_api_key"; + final String apiSecret = "your_api_secret"; + final String callback = "your_callback"; + final String baseUrl = "your_base_url"; + final String realm = "your_realm"; + + final String protectedResourceUrl = baseUrl + "/auth/realms/" + realm + "/protocol/openid-connect/userinfo"; + + final OAuth20Service service = new ServiceBuilder(apiKey) + .apiSecret(apiSecret) + .defaultScope("openid") + .callback(callback) + .build(KeycloakApi.instance(baseUrl, realm)); + final Scanner in = new Scanner(System.in); + + System.out.println("=== Keyloack's OAuth Workflow ==="); + System.out.println(); + + // Obtain the Authorization URL + System.out.println("Fetching the Authorization URL..."); + final String authorizationUrl = service.getAuthorizationUrl(); + 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(); + + // 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, protectedResourceUrl); + 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! :)"); + + } +} 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 8553c3498..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,31 +3,35 @@ 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; import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth20Service; import java.io.IOException; +import java.util.Random; import java.util.concurrent.ExecutionException; -public final class LinkedIn20Example { +public class LinkedIn20Example { private static final String NETWORK_NAME = "LinkedIn"; - private static final String PROTECTED_RESOURCE_URL = "https://api.linkedin.com/v1/people/~:(%s)"; + private static final String PROTECTED_RESOURCE_URL = "https://api.linkedin.com/v2/me"; + private static final String PROTECTED_EMAIL_RESOURCE_URL + = "https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))"; private LinkedIn20Example() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your client id and secret final String clientId = "your client id"; final String clientSecret = "your client secret"; final OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) - .scope("r_basicprofile r_emailaddress") // replace with desired scope + .defaultScope(new ScopeBuilder("r_liteprofile", "r_emailaddress")) // replace with desired scope .callback("http://example.com/callback") - .state("some_params") .build(LinkedInApi20.instance()); final Scanner in = new Scanner(System.in); @@ -36,7 +40,8 @@ public static void main(String... args) throws IOException, InterruptedException // Obtain the Authorization URL System.out.println("Fetching the Authorization URL..."); - final String authorizationUrl = service.getAuthorizationUrl(); + final String secretState = "secret" + new Random().nextInt(999_999); + 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); @@ -45,36 +50,36 @@ public static void main(String... args) throws IOException, InterruptedException final String code = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token - System.out.println("Trading the Request Token for an Access Token..."); + 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("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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..."); - while (true) { - System.out.println("Paste profile query for fetch (firstName, lastName, etc) or 'exit' to stop example"); - System.out.print(">>"); - final String query = in.nextLine(); - System.out.println(); + System.out.println("Now we're going to get the email of the current user..."); + final OAuthRequest emailRequest = new OAuthRequest(Verb.GET, PROTECTED_EMAIL_RESOURCE_URL); + emailRequest.addHeader("x-li-format", "json"); + emailRequest.addHeader("Accept-Language", "ru-RU"); + service.signRequest(accessToken, emailRequest); + System.out.println(); + try (Response emailResponse = service.execute(emailRequest)) { + System.out.println(emailResponse.getCode()); + System.out.println(emailResponse.getBody()); + } + System.out.println(); - if ("exit".equals(query)) { - break; - } + System.out.println("Now we're going to access a protected profile resource..."); - final OAuthRequest request = new OAuthRequest(Verb.GET, String.format(PROTECTED_RESOURCE_URL, query)); - request.addHeader("x-li-format", "json"); - request.addHeader("Accept-Language", "ru-RU"); - service.signRequest(accessToken, request); - final Response response = service.execute(request); - System.out.println(); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); + request.addHeader("x-li-format", "json"); + request.addHeader("Accept-Language", "ru-RU"); + service.signRequest(accessToken, request); + System.out.println(); + try (Response response = service.execute(request)) { System.out.println(response.getCode()); System.out.println(response.getBody()); - - System.out.println(); } + + System.out.println(); } } diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/LinkedInExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/LinkedInExample.java index 02ae520c1..ec1051134 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/LinkedInExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/LinkedInExample.java @@ -12,7 +12,7 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class LinkedInExample { +public class LinkedInExample { private static final String PROTECTED_RESOURCE_URL = "http://api.linkedin.com/v1/people/~/connections:(id,last-name)"; @@ -20,6 +20,7 @@ public final class LinkedInExample { private LinkedInExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { final OAuth10aService service = new ServiceBuilder("your client id") .apiSecret("your client secret") @@ -42,22 +43,22 @@ public static void main(String... args) throws IOException, InterruptedException final String oauthVerifier = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token + // Trade the Request Token and Verifier for the Access Token System.out.println("Trading the Request Token for an Access Token..."); final OAuth1AccessToken accessToken = service.getAccessToken(requestToken, oauthVerifier); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final Response response = service.execute(request); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getBody()); + try (Response response = service.execute(request)) { + System.out.println("Got it! Lets see what we found..."); + System.out.println(); + 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/LinkedInExampleWithScopes.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/LinkedInExampleWithScopes.java index a8247f65c..3b795de1f 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/LinkedInExampleWithScopes.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/LinkedInExampleWithScopes.java @@ -12,7 +12,7 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class LinkedInExampleWithScopes { +public class LinkedInExampleWithScopes { private static final String PROTECTED_RESOURCE_URL = "http://api.linkedin.com/v1/people/~/connections:(id,last-name)"; @@ -20,6 +20,7 @@ public final class LinkedInExampleWithScopes { private LinkedInExampleWithScopes() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your client id and secret final String clientId = "your client id"; @@ -46,22 +47,22 @@ public static void main(String... args) throws IOException, InterruptedException final String oauthVerifier = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token + // Trade the Request Token and Verifier for the Access Token System.out.println("Trading the Request Token for an Access Token..."); final OAuth1AccessToken accessToken = service.getAccessToken(requestToken, oauthVerifier); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final Response response = service.execute(request); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getBody()); + try (Response response = service.execute(request)) { + System.out.println("Got it! Lets see what we found..."); + System.out.println(); + 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/LiveExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/LiveExample.java index 09fabe03a..2c8635a1a 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/LiveExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/LiveExample.java @@ -11,20 +11,21 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class LiveExample { +public class LiveExample { private static final String PROTECTED_RESOURCE_URL = "https://apis.live.net/v5.0/me"; private LiveExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your own api key and secret final String apiKey = ""; final String apiSecret = ""; final OAuth20Service service = new ServiceBuilder(apiKey) .apiSecret(apiSecret) - .scope("wl.basic") + .defaultScope("wl.basic") .callback("http://localhost:9000/") .build(LiveApi.instance()); final Scanner in = new Scanner(System.in); @@ -43,23 +44,22 @@ public static void main(String... args) throws IOException, InterruptedException final String code = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token - System.out.println("Trading the Request Token for an Access Token..."); + 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("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final 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()); + 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! :)"); diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MailruAsyncExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MailruAsyncExample.java index 2c566e99b..1ad9f56cc 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MailruAsyncExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MailruAsyncExample.java @@ -13,7 +13,7 @@ import com.github.scribejava.core.oauth.OAuth20Service; import java.io.IOException; -public final class MailruAsyncExample { +public class MailruAsyncExample { private static final String NETWORK_NAME = "Mail.ru"; private static final String PROTECTED_RESOURCE_URL @@ -22,6 +22,7 @@ public final class MailruAsyncExample { private MailruAsyncExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws InterruptedException, ExecutionException, IOException { // Replace these with your client id and secret final String clientId = "your client id"; @@ -56,23 +57,23 @@ public static void main(String... args) throws InterruptedException, ExecutionEx final String code = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token - System.out.println("Trading the Request Token for an Access Token..."); + 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("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + System.out.println("(The raw response looks like this: " + accessToken.getRawResponse() + + "')"); System.out.println(); 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); - final 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()); + 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! :)"); diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MailruExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MailruExample.java index f6c620d0f..4eb4c7aff 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MailruExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MailruExample.java @@ -11,7 +11,7 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class MailruExample { +public class MailruExample { private static final String NETWORK_NAME = "Mail.ru"; private static final String PROTECTED_RESOURCE_URL @@ -20,6 +20,7 @@ public final class MailruExample { private MailruExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your client id and secret final String clientId = "your client id"; @@ -45,22 +46,21 @@ public static void main(String... args) throws IOException, InterruptedException final String code = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token - System.out.println("Trading the Request Token for an Access Token..."); + 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("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + System.out.println("(The raw response looks like this: " + accessToken.getRawResponse() + "')"); System.out.println(); 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); - final 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()); + 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! :)"); diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MediaWikiExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MediaWikiExample.java new file mode 100644 index 000000000..621480013 --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MediaWikiExample.java @@ -0,0 +1,72 @@ +package com.github.scribejava.apis.examples; + +import java.util.Scanner; +import com.github.scribejava.core.builder.ServiceBuilder; +import com.github.scribejava.apis.MediaWikiApi; +import com.github.scribejava.core.model.OAuth1AccessToken; +import com.github.scribejava.core.model.OAuth1RequestToken; +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.OAuth10aService; +import java.io.IOException; +import java.util.concurrent.ExecutionException; + +public class MediaWikiExample { + + // To get your consumer key/secret, see https://meta.wikimedia.org/wiki/Special:OAuthConsumerRegistration/propose + private static final String CONSUMER_KEY = ""; + private static final String CONSUMER_SECRET = ""; + + private static final String API_USERINFO_URL + = "https://meta.wikimedia.org/w/api.php?action=query&format=json&meta=userinfo"; + + private MediaWikiExample() { + } + + @SuppressWarnings("PMD.SystemPrintln") + public static void main(String... args) throws IOException, InterruptedException, ExecutionException { + final OAuth10aService service = new ServiceBuilder(CONSUMER_KEY) + .apiSecret(CONSUMER_SECRET) + .build(MediaWikiApi.instance()); + + final Scanner in = new Scanner(System.in); + + System.out.println("=== MediaWiki's OAuth Workflow ==="); + System.out.println(); + + // Obtain the Request Token + System.out.println("Fetching the Request Token..."); + final OAuth1RequestToken requestToken = service.getRequestToken(); + System.out.println("Got the Request Token!"); + System.out.println(); + + System.out.println("Now go and authorize ScribeJava here:"); + System.out.println(service.getAuthorizationUrl(requestToken)); + System.out.println("And paste the verifier here"); + System.out.print(">>"); + final String oauthVerifier = in.nextLine(); + System.out.println(); + + // Trade the Request Token and Verifier for the Access Token + System.out.println("Trading the Request Token for an Access Token..."); + final OAuth1AccessToken accessToken = service.getAccessToken(requestToken, oauthVerifier); + 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, API_USERINFO_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.getBody()); + } + + System.out.println(); + System.out.println("Thats it man! Go and build something awesome with MediaWiki and ScribeJava! :)"); + } + +} diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Meetup20Example.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Meetup20Example.java new file mode 100644 index 000000000..f64ec246b --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Meetup20Example.java @@ -0,0 +1,70 @@ +package com.github.scribejava.apis.examples; + +import com.github.scribejava.apis.MeetupApi20; +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.Random; +import java.util.Scanner; +import java.util.concurrent.ExecutionException; + +public class Meetup20Example { + + private static final String NETWORK_NAME = "Meetup"; + private static final String PROTECTED_RESOURCE_URL = " https://api.meetup.com/self/groups"; + + private Meetup20Example() { + } + + @SuppressWarnings("PMD.SystemPrintln") + public static void main(String... args) throws IOException, InterruptedException, ExecutionException { + // Replace these with your Meetup consumer id and secret + final String consumerKey = "your consumer key"; + final String consumerSecret = "your consumer secret"; + final OAuth20Service service = new ServiceBuilder(consumerKey) + .apiSecret(consumerSecret) + .defaultScope("basic") // replace with desired scopes + .callback("http://example.com/callback") // replace with appropriate URI for your consumer, + // see https://www.meetup.com/meetup_api/auth/#redirect_uris + .build(MeetupApi20.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 String secretState = "secret" + new Random().nextInt(999_999); + 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("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("Now we're going to the user's groups...."); + + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); + service.signRequest(accessToken, request); + System.out.println(); + try (Response response = service.execute(request)) { + System.out.println(response.getCode()); + System.out.println(response.getBody()); + } + + System.out.println(); + } +} diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MeetupExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MeetupExample.java index c8d6ea911..e3c7c8e8c 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MeetupExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MeetupExample.java @@ -12,13 +12,14 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class MeetupExample { +public class MeetupExample { private static final String PROTECTED_RESOURCE_URL = "http://api.meetup.com/2/member/self"; private MeetupExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { final OAuth10aService service = new ServiceBuilder("your client id") .apiSecret("your client secret") @@ -41,22 +42,22 @@ public static void main(String... args) throws IOException, InterruptedException final String oauthVerifier = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token + // Trade the Request Token and Verifier for the Access Token System.out.println("Trading the Request Token for an Access Token..."); final OAuth1AccessToken accessToken = service.getAccessToken(requestToken, oauthVerifier); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final Response response = service.execute(request); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getBody()); + try (Response response = service.execute(request)) { + System.out.println("Got it! Lets see what we found..."); + System.out.println(); + 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/MicrosoftAzureActiveDirectory20Example.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MicrosoftAzureActiveDirectory20Example.java new file mode 100644 index 000000000..71f346bc6 --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MicrosoftAzureActiveDirectory20Example.java @@ -0,0 +1,63 @@ +package com.github.scribejava.apis.examples; + +import com.github.scribejava.apis.MicrosoftAzureActiveDirectory20Api; +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.Scanner; +import java.util.concurrent.ExecutionException; + +public class MicrosoftAzureActiveDirectory20Example { + + private static final String NETWORK_NAME = "Microsoft Azure Active Directory"; + private static final String PROTECTED_RESOURCE_URL = "https://graph.microsoft.com/v1.0/me"; + + private MicrosoftAzureActiveDirectory20Example() { + } + + @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 here"; + final String clientSecret = "client secret here"; + final OAuth20Service service = new ServiceBuilder(clientId) + .apiSecret(clientSecret) + .defaultScope("openid User.Read") + .callback("http://www.example.com/oauth_callback/") + .build(MicrosoftAzureActiveDirectory20Api.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(); + 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(); + // 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! :)"); + } +} diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MicrosoftAzureActiveDirectoryExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MicrosoftAzureActiveDirectoryExample.java new file mode 100644 index 000000000..6c9610f06 --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MicrosoftAzureActiveDirectoryExample.java @@ -0,0 +1,70 @@ +package com.github.scribejava.apis.examples; + +import com.github.scribejava.apis.MicrosoftAzureActiveDirectoryApi; +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.Scanner; +import java.util.concurrent.ExecutionException; + +public class MicrosoftAzureActiveDirectoryExample { + + private static final String NETWORK_NAME = "Microsoft Azure Active Directory"; + private static final String PROTECTED_RESOURCE_URL = "https://graph.windows.net/me?api-version=1.6"; + + private MicrosoftAzureActiveDirectoryExample() { + + } + + @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 here"; + final String clientSecret = "client secret here"; + final OAuth20Service service = new ServiceBuilder(clientId) + .apiSecret(clientSecret) + .defaultScope("openid") + .callback("http://www.example.com/oauth_callback/") + .build(MicrosoftAzureActiveDirectoryApi.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(); + 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(); + + // 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! :)"); + } +} diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MisfitExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MisfitExample.java index ed6c1dd7f..65e048499 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MisfitExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/MisfitExample.java @@ -12,7 +12,7 @@ import java.util.Scanner; import java.util.concurrent.ExecutionException; -public final class MisfitExample { +public class MisfitExample { private static final String NETWORK_NAME = "Misfit"; private static final String PROTECTED_RESOURCE_URL @@ -21,6 +21,7 @@ public final class MisfitExample { private MisfitExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your own api key and secret final String apiKey = "your client id"; @@ -28,7 +29,7 @@ public static void main(String... args) throws IOException, InterruptedException final OAuth20Service service = new ServiceBuilder(apiKey) .apiSecret(apiSecret) .callback("http://example.com/callback/") - .scope("public,birthday,email,tracking,session,sleep") + .defaultScope("public,birthday,email,tracking,session,sleep") .build(MisfitApi.instance()); final Scanner in = new Scanner(System.in); @@ -50,19 +51,19 @@ public static void main(String... args) throws IOException, InterruptedException System.out.println("Trading the Request Token for an Access Token..."); final OAuth2AccessToken accessToken = service.getAccessToken(code); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final 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()); + 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! :)"); diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/NaverExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/NaverExample.java index 5d2d5548e..7918ebd02 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/NaverExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/NaverExample.java @@ -13,7 +13,7 @@ import java.util.Scanner; import java.util.concurrent.ExecutionException; -public final class NaverExample { +public class NaverExample { private static final String NETWORK_NAME = "Naver"; private static final String PROTECTED_RESOURCE_URL = "https://openapi.naver.com/v1/nid/me"; @@ -21,6 +21,7 @@ public final class NaverExample { private NaverExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your client id and secret final String clientId = "your client id"; @@ -28,7 +29,6 @@ public static void main(String... args) throws IOException, InterruptedException final String secretState = "secret" + new Random().nextInt(999_999); final OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) - .state(secretState) .callback("http://www.example.com/oauth_callback/") .build(NaverApi.instance()); @@ -39,7 +39,7 @@ public static void main(String... args) throws IOException, InterruptedException // Obtain the Authorization URL System.out.println("Fetching the Authorization URL..."); - final String authorizationUrl = service.getAuthorizationUrl(); + 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); @@ -60,23 +60,22 @@ public static void main(String... args) throws IOException, InterruptedException System.out.println(); } - // Trade the Request Token and Verfier for the Access Token - System.out.println("Trading the Request Token for an Access Token..."); + 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("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final 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()); + 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! :)"); diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/NeteaseWeiboExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/NeteaseWeiboExample.java deleted file mode 100644 index 866c2167b..000000000 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/NeteaseWeiboExample.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.github.scribejava.apis.examples; - -import java.util.Scanner; -import com.github.scribejava.core.builder.ServiceBuilder; -import com.github.scribejava.apis.NeteaseWeibooApi; -import com.github.scribejava.core.model.OAuth1AccessToken; -import com.github.scribejava.core.model.OAuth1RequestToken; -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.OAuth10aService; -import java.io.IOException; -import java.util.concurrent.ExecutionException; - -public final class NeteaseWeiboExample { - - private static final String NETWORK_NAME = "NetEase(163.com) Weibo"; - private static final String PROTECTED_RESOURCE_URL = "http://api.t.163.com/account/verify_credentials.json"; - - private NeteaseWeiboExample() { - } - - public static void main(String... args) throws IOException, InterruptedException, ExecutionException { - // Replace these with your own api key and secret - final String apiKey = "your key"; - final String apiSecret = "your secret"; - final OAuth10aService service = new ServiceBuilder(apiKey) - .apiSecret(apiSecret) - .build(NeteaseWeibooApi.instance()); - final Scanner in = new Scanner(System.in); - - System.out.println("=== " + NETWORK_NAME + "'s OAuth Workflow ==="); - System.out.println(); - - // Grab a request token. - System.out.println("Fetching request token."); - final OAuth1RequestToken requestToken = service.getRequestToken(); - System.out.println("Got it ... "); - System.out.println(requestToken.getToken()); - - // Obtain the Authorization URL - System.out.println("Fetching the Authorization URL..."); - final String authorizationUrl = service.getAuthorizationUrl(requestToken); - 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 oauthVerifier = in.nextLine(); - System.out.println(); - - // Trade the Request Token and Verfier for the Access Token - System.out.println("Trading the Request Token for an Access Token..."); - final OAuth1AccessToken accessToken = service.getAccessToken(requestToken, oauthVerifier); - System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + 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); - final 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! :)"); - } -} diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/OdnoklassnikiExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/OdnoklassnikiExample.java index 8ea048fe8..ff2c05863 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/OdnoklassnikiExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/OdnoklassnikiExample.java @@ -11,7 +11,7 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class OdnoklassnikiExample { +public class OdnoklassnikiExample { private static final String NETWORK_NAME = "Odnoklassniki.ru"; private static final String PROTECTED_RESOURCE_URL @@ -20,9 +20,10 @@ public final class OdnoklassnikiExample { private OdnoklassnikiExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your client id and secret - final String clientId = "your api client id"; + final String clientId = "your api client id"; final String publicKey = "your api public key"; final String secretKey = "your api secret key"; @@ -47,31 +48,28 @@ public static void main(String... args) throws IOException, InterruptedException final String code = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token - System.out.println("Trading the Request Token for an Access Token..."); - + System.out.println("Trading the Authorization Code for an Access Token..."); OAuth2AccessToken accessToken = service.getAccessToken(code); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + System.out.println("(The raw response looks like this: " + accessToken.getRawResponse() + "')"); System.out.println(); System.out.println("Refreshing the Access Token..."); accessToken = service.refreshAccessToken(accessToken.getRefreshToken()); System.out.println("Refreshed the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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, String.format(PROTECTED_RESOURCE_URL, publicKey)); service.signRequest(accessToken, request); - final 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()); + 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! :)"); diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/PinterestExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/PinterestExample.java index 1fec439b4..73ad113d5 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/PinterestExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/PinterestExample.java @@ -12,21 +12,22 @@ import java.util.Scanner; import java.util.concurrent.ExecutionException; -public final class PinterestExample { +public class PinterestExample { - private static final String PROTECTED_RESOURCE_URL = "https://api.pinterest.com/v1/me/?access_token?access_token="; + private static final String PROTECTED_RESOURCE_URL = "https://api.pinterest.com/v1/me/"; private PinterestExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your own api key and secret final String apiKey = "your_app_id"; final String apiSecret = "your_app_secret"; final OAuth20Service service = new ServiceBuilder(apiKey) .apiSecret(apiSecret) - .scope("read_public,write_public,read_relationships,write_relationships") - .callback("https://localhost:9000/") // Add as valid callback in developer portal + .defaultScope("read_public,write_public,read_relationships,write_relationships") + .callback("https://localhost/") // Add as valid callback in developer portal .build(PinterestApi.instance()); final Scanner in = new Scanner(System.in); @@ -44,23 +45,22 @@ public static void main(String... args) throws IOException, InterruptedException final String code = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token - System.out.println("Trading the Request Token for an Access Token..."); + 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("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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 + accessToken.getAccessToken()); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final 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()); + 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! :)"); diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/PolarAPIExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/PolarAPIExample.java new file mode 100644 index 000000000..501a8e470 --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/PolarAPIExample.java @@ -0,0 +1,82 @@ +package com.github.scribejava.apis.examples; + +import com.github.scribejava.apis.PolarAPI; +import com.github.scribejava.apis.polar.PolarOAuth2AccessToken; +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.util.Scanner; + +public class PolarAPIExample { + + private static final String NETWORK_NAME = "Polar"; + + private static final String PROTECTED_RESOURCE_URL = "https://www.polaraccesslink.com/v3/users/%s"; + + private PolarAPIExample() { + } + + @SuppressWarnings("PMD.SystemPrintln") + public static void main(String... args) throws Exception { + final String clientId = "your_api_client"; + final String clientSecret = "your_api_secret"; + final String scope = "accesslink.read_all"; + final String callback = "your_api_callback"; + final OAuth20Service service = new ServiceBuilder(clientId) + .apiSecret(clientSecret) + .defaultScope(scope) + //your callback URL to store and handle the authorization code sent by Polar + .callback(callback) + .build(PolarAPI.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 String authorizationUrl = service.getAuthorizationUrl("some_params"); + 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..."); + System.out.println("Got the Access Token!"); + final OAuth2AccessToken oauth2AccessToken = service.getAccessToken(code); + System.out.println("(if your curious it looks like this: " + oauth2AccessToken + + ", 'rawResponse'='" + oauth2AccessToken.getRawResponse() + "')"); + System.out.println(); + + if (!(oauth2AccessToken instanceof PolarOAuth2AccessToken)) { + System.out.println("oauth2AccessToken is not instance of PolarOAuth2AccessToken. Strange enough. exit."); + return; + } + + final PolarOAuth2AccessToken accessToken = (PolarOAuth2AccessToken) oauth2AccessToken; + + // Now let's go and ask for a protected resource! + // This will get the profile for this user + System.out.println("Now we're going to access a protected resource..."); + + final OAuthRequest request = new OAuthRequest(Verb.GET, String.format(PROTECTED_RESOURCE_URL, + accessToken.getUserId())); + request.addHeader("Accept", "application/json"); + + service.signRequest(accessToken, request); + + System.out.println(); + try (Response response = service.execute(request)) { + System.out.println(response.getCode()); + System.out.println(response.getBody()); + } + System.out.println(); + } +} diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Px500Example.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Px500Example.java index 820a0e821..e441cebdd 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Px500Example.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Px500Example.java @@ -12,13 +12,14 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class Px500Example { +public class Px500Example { private static final String PROTECTED_RESOURCE_URL = "https://api.500px.com/v1/"; private Px500Example() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { final OAuth10aService service = new ServiceBuilder("your-api-key") .apiSecret("your-api-secret") @@ -41,22 +42,22 @@ public static void main(String... args) throws IOException, InterruptedException final String oauthVerifier = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token + // Trade the Request Token and Verifier for the Access Token System.out.println("Trading the Request Token for an Access Token..."); final OAuth1AccessToken accessToken = service.getAccessToken(requestToken, oauthVerifier); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final Response response = service.execute(request); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getBody()); + try (Response response = service.execute(request)) { + System.out.println("Got it! Lets see what we found..."); + System.out.println(); + 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/RenrenExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/RenrenExample.java index 1eea7e581..701be48b4 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/RenrenExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/RenrenExample.java @@ -3,10 +3,7 @@ import java.nio.charset.Charset; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Scanner; import com.github.scribejava.core.builder.ServiceBuilder; @@ -18,9 +15,13 @@ import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth20Service; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Formatter; +import java.util.List; import java.util.concurrent.ExecutionException; -public final class RenrenExample { +public class RenrenExample { private static final String NETWORK_NAME = "Renren"; private static final String PROTECTED_RESOURCE_URL = "http://api.renren.com/restserver.do"; @@ -28,13 +29,14 @@ public final class RenrenExample { private RenrenExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your own api key and secret final String apiKey = "your api key"; final String apiSecret = "your api secret"; final OAuth20Service service = new ServiceBuilder(apiKey) .apiSecret(apiSecret) - .scope("status_update publish_feed") + .defaultScope("status_update publish_feed") .callback("http://your.doman.com/oauth/renren") .build(RenrenApi.instance()); final Scanner in = new Scanner(System.in); @@ -53,12 +55,10 @@ public static void main(String... args) throws IOException, InterruptedException final String code = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token - System.out.println("Trading the Request Token for an Access Token..."); + 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("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + System.out.println("(The raw response looks like this: " + accessToken.getRawResponse() + "')"); System.out.println(); // Now let's go and ask for a protected resource! @@ -70,25 +70,28 @@ public static void main(String... args) throws IOException, InterruptedException parameters.put("v", "1.0"); final List sigString = new ArrayList<>(parameters.size() + 1); - for (Map.Entry entry : parameters.entrySet()) { - request.addQuerystringParameter(entry.getKey(), entry.getValue()); - sigString.add(String.format("%s=%s", entry.getKey(), entry.getValue())); + for (Map.Entry parameter : parameters.entrySet()) { + request.addQuerystringParameter(parameter.getKey(), parameter.getValue()); + sigString.add(String.format("%s=%s", parameter.getKey(), parameter.getValue())); } sigString.add(String.format("%s=%s", OAuthConstants.ACCESS_TOKEN, accessToken.getAccessToken())); Collections.sort(sigString); - final StringBuilder b = new StringBuilder(); + final StringBuilder sigBuilder = new StringBuilder(); for (String param : sigString) { - b.append(param); + sigBuilder.append(param); } - b.append(apiSecret); - System.out.println("Sig string: " + b.toString()); - request.addQuerystringParameter("sig", md5(b.toString())); + sigBuilder.append(apiSecret); + + final String sig = sigBuilder.toString(); + System.out.println("Sig string: " + sig); + request.addQuerystringParameter("sig", md5(sig)); service.signRequest(accessToken, request); - final 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()); + 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! :)"); @@ -99,15 +102,13 @@ public static String md5(String orgString) { try { final MessageDigest md = MessageDigest.getInstance("MD5"); final byte[] array = md.digest(orgString.getBytes(Charset.forName("UTF-8"))); - final StringBuffer sb = new StringBuffer(); - for (int i = 0; i < array.length; ++i) { - sb.append(Integer.toHexString((array[i] & 0xFF) | 0x100).substring(1, 3)); + final Formatter builder = new Formatter(); + for (byte b : array) { + builder.format("%02x", b); } - return sb.toString(); + return builder.toString(); } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); + throw new IllegalStateException("MD5 is unsupported?", e); } - return null; } - } diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SalesforceExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SalesforceExample.java index 30f4aab85..6e8ce342a 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SalesforceExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SalesforceExample.java @@ -17,19 +17,20 @@ import java.security.NoSuchAlgorithmException; import java.util.concurrent.ExecutionException; -public final class SalesforceExample { +public class SalesforceExample { private static final String NETWORK_NAME = "Salesforce"; private SalesforceExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, NoSuchAlgorithmException, KeyManagementException, InterruptedException, ExecutionException { // Replace these with your client id and secret final String clientId = "your client id"; final String clientSecret = "your client secret"; - //IT's important! Salesforce upper require TLS v1.1 or 1.2. + //IT's important! Salesforce upper require TLS v1.1 or higher. //They are enabled in Java 8 by default, but not in Java 7 SalesforceApi.initTLSv11orUpper(); @@ -38,7 +39,6 @@ public static void main(String... args) throws IOException, NoSuchAlgorithmExcep // // When you plan to connect to a Sandbox environment you've to use SalesforceApi.sandbox() API instance // new ServiceBuilder.....build(SalesforceApi.sandbox()); - final OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) .callback("https://www.example.com/callback") @@ -76,8 +76,7 @@ public static void main(String... args) throws IOException, NoSuchAlgorithmExcep } System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + salesforceAccessToken + ", 'rawResponse'='" - + accessToken.getRawResponse() + "')"); + System.out.println("(The raw response looks like this: " + accessToken.getRawResponse() + "')"); System.out.println(); System.out.println("instance_url is: " + salesforceAccessToken.getInstanceUrl()); @@ -95,9 +94,11 @@ public static void main(String... args) throws IOException, NoSuchAlgorithmExcep System.out.println("Full URL: " + url); final OAuthRequest request = new OAuthRequest(Verb.GET, url); - final Response response = service.execute(request); + service.signRequest(salesforceAccessToken, request); System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); + try (Response response = service.execute(request)) { + System.out.println(response.getCode()); + System.out.println(response.getBody()); + } } } diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SalesforceNingAsyncExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SalesforceNingAsyncExample.java index d057df0d9..6c3987046 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SalesforceNingAsyncExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SalesforceNingAsyncExample.java @@ -20,14 +20,14 @@ import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; -public final class SalesforceNingAsyncExample { +public class SalesforceNingAsyncExample { private static final String NETWORK_NAME = "Salesforce"; private SalesforceNingAsyncExample() { } - @SuppressWarnings({"unchecked", "rawtypes"}) + @SuppressWarnings({"unchecked", "rawtypes", "PMD.SystemPrintln"}) public static void main(String... args) throws InterruptedException, ExecutionException, UnsupportedEncodingException, IOException, NoSuchAlgorithmException, KeyManagementException { // Replace these with your client id and secret @@ -42,7 +42,7 @@ public static void main(String... args) throws InterruptedException, ExecutionEx .setReadTimeout(10_000) .build()); - //IT's important! Salesforce upper require TLS v1.1 or 1.2 + //IT's important! Salesforce upper require TLS v1.1 or higher SalesforceApi.initTLSv11orUpper(); try (OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) @@ -77,8 +77,8 @@ public static void main(String... args) throws InterruptedException, ExecutionEx } System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + salesforceAccessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + System.out.println("(The raw response looks like this: " + accessToken.getRawResponse() + + "')"); System.out.println(); System.out.println("Instance is: " + salesforceAccessToken.getInstanceUrl()); // Now let's go and ask for a protected resource! @@ -91,10 +91,11 @@ public static void main(String... args) throws InterruptedException, ExecutionEx System.out.println(); System.out.println("Full URL: " + url); final OAuthRequest request = new OAuthRequest(Verb.GET, url); - final Response response = service.execute(request); System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); + try (Response response = service.execute(request)) { + System.out.println(response.getCode()); + System.out.println(response.getBody()); + } } } } diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SinaWeibo2Example.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SinaWeibo2Example.java index a2bacaff4..3ac982efb 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SinaWeibo2Example.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SinaWeibo2Example.java @@ -11,7 +11,7 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class SinaWeibo2Example { +public class SinaWeibo2Example { private static final String NETWORK_NAME = "SinaWeibo"; private static final String PROTECTED_RESOURCE_URL = "https://api.weibo.com/2/account/get_uid.json"; @@ -19,6 +19,7 @@ public final class SinaWeibo2Example { private SinaWeibo2Example() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your own api key and secret final String apiKey = "your_api_key"; @@ -47,19 +48,19 @@ public static void main(String... args) throws IOException, InterruptedException System.out.println("Trading the Request Token for an Access Token..."); final OAuth2AccessToken accessToken = service.getAccessToken(code); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final 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()); + 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! :)"); diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SinaWeiboExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SinaWeiboExample.java index 289f4eb50..767ccdbab 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SinaWeiboExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SinaWeiboExample.java @@ -12,7 +12,7 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class SinaWeiboExample { +public class SinaWeiboExample { private static final String NETWORK_NAME = "SinaWeibo"; private static final String PROTECTED_RESOURCE_URL = "http://api.t.sina.com.cn/account/verify_credentials.json"; @@ -20,6 +20,7 @@ public final class SinaWeiboExample { private SinaWeiboExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your own api key and secret final String apiKey = "your key"; @@ -49,23 +50,23 @@ public static void main(String... args) throws IOException, InterruptedException final String oauthVerifier = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token + // Trade the Request Token and Verifier for the Access Token System.out.println("Trading the Request Token for an Access Token..."); final OAuth1AccessToken accessToken = service.getAccessToken(requestToken, oauthVerifier); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final 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()); + 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! :)"); diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SkyrockExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SkyrockExample.java index fcb2f592a..f2c0cf007 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SkyrockExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SkyrockExample.java @@ -12,13 +12,14 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class SkyrockExample { +public class SkyrockExample { private static final String PROTECTED_RESOURCE_URL = "https://api.skyrock.com/v2/user/get.json"; private SkyrockExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { final OAuth10aService service = new ServiceBuilder("your-api-key") .apiSecret("your-api-secret") @@ -41,23 +42,23 @@ public static void main(String... args) throws IOException, InterruptedException final String oauthVerifier = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token + // Trade the Request Token and Verifier for the Access Token System.out.println("Trading the Request Token for an Access Token..."); final OAuth1AccessToken accessToken = service.getAccessToken(requestToken, oauthVerifier); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final 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()); + 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! :)"); 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/StackExchangeExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/StackExchangeExample.java index 04858eab6..5b4d52bcb 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/StackExchangeExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/StackExchangeExample.java @@ -13,7 +13,7 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class StackExchangeExample { +public class StackExchangeExample { private static final String NETWORK_NAME = "Stack Exchange"; private static final String PROTECTED_RESOURCE_URL = "https://api.stackexchange.com/2.2/me"; @@ -21,6 +21,7 @@ public final class StackExchangeExample { private StackExchangeExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your client id, secret, application key and // optionally site name @@ -32,7 +33,6 @@ public static void main(String... args) throws IOException, InterruptedException final String secretState = "secret" + new Random().nextInt(999_999); final OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) - .state(secretState) .callback("http://www.example.com/oauth_callback/") .build(StackExchangeApi.instance()); final Scanner in = new Scanner(System.in, "UTF-8"); @@ -42,7 +42,7 @@ public static void main(String... args) throws IOException, InterruptedException // Obtain the Authorization URL System.out.println("Fetching the Authorization URL..."); - final String authorizationUrl = service.getAuthorizationUrl(); + 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); @@ -63,12 +63,10 @@ public static void main(String... args) throws IOException, InterruptedException System.out.println(); } - // Trade the Request Token and Verfier for the Access Token - System.out.println("Trading the Request Token for an Access Token..."); + 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("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + System.out.println("(The raw response looks like this: " + accessToken.getRawResponse() + "')"); System.out.println(); // Now let's go and ask for a protected resource! @@ -76,11 +74,12 @@ public static void main(String... args) throws IOException, InterruptedException final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL + "?site=" + site + "&key=" + key); service.signRequest(accessToken, request); - final 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()); + 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! :)"); diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TheThingsNetworkV1StagingExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TheThingsNetworkV1StagingExample.java index 057f9b27c..63139e589 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TheThingsNetworkV1StagingExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TheThingsNetworkV1StagingExample.java @@ -14,7 +14,7 @@ import java.util.Scanner; import java.util.concurrent.ExecutionException; -public final class TheThingsNetworkV1StagingExample { +public class TheThingsNetworkV1StagingExample { private static final String NETWORK_NAME = "TTNv1staging"; private static final String PROTECTED_RESOURCE_URL = "https://account.thethingsnetwork.org/applications"; @@ -22,6 +22,7 @@ public final class TheThingsNetworkV1StagingExample { private TheThingsNetworkV1StagingExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your client id and secret final String clientId = "your_client_id"; @@ -31,7 +32,6 @@ public static void main(String... args) throws IOException, InterruptedException final OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) - .state(secretState) .callback(redirectURI) .build(TheThingsNetworkV1StagingApi.instance()); final Scanner in = new Scanner(System.in, "UTF-8"); @@ -41,7 +41,7 @@ public static void main(String... args) throws IOException, InterruptedException // Obtain the Authorization URL System.out.println("Fetching the Authorization URL..."); - final String authorizationUrl = service.getAuthorizationUrl(); + 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); @@ -50,7 +50,7 @@ public static void main(String... args) throws IOException, InterruptedException // TTN v1staging does not have URL safe keys, so we have to decode it final String code = URLDecoder.decode(in.nextLine(), "UTF-8"); - System.out.println("Using code: "+code); + System.out.println("Using code: " + code); System.out.println(); System.out.println("And paste the state from server here. We have set 'secretState'='" + secretState + "'."); @@ -69,8 +69,7 @@ public static void main(String... args) throws IOException, InterruptedException System.out.println("Trading the Request Token for an Access Token..."); final OAuth2AccessToken accessToken = service.getAccessToken(code); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + System.out.println("(The raw response looks like this: " + accessToken.getRawResponse() + "')"); System.out.println(); // Now let's go and ask for a protected resource! @@ -79,19 +78,20 @@ public static void main(String... args) throws IOException, InterruptedException service.signRequest(accessToken, request); request.addHeader("Accept", "application/json"); - final Response response = service.execute(request); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); + try (Response response = service.execute(request)) { + System.out.println("Got it! Lets see what we found..."); + System.out.println(); + System.out.println(response.getCode()); - if (response.getCode() == 401) { - System.out.println("Not authorised: "+response.getBody()); - } else { - System.out.println("You should see a JSON array of your registered applications:"); - System.out.println(response.getBody()); + if (response.getCode() == 401) { + System.out.println("Not authorised: " + response.getBody()); + } else { + System.out.println("You should see a JSON array of your registered applications:"); + System.out.println(response.getBody()); - System.out.println(); - System.out.println("That's it man! Go and build something awesome with ScribeJava! :)"); + System.out.println(); + System.out.println("That's it man! Go and build something awesome with ScribeJava! :)"); + } } } } diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TheThingsNetworkV2PreviewExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TheThingsNetworkV2PreviewExample.java index acb791c1c..25316d6ea 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TheThingsNetworkV2PreviewExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TheThingsNetworkV2PreviewExample.java @@ -13,15 +13,16 @@ import java.util.Scanner; import java.util.concurrent.ExecutionException; -public final class TheThingsNetworkV2PreviewExample { +public class TheThingsNetworkV2PreviewExample { private static final String NETWORK_NAME = "TTNv2preview"; - private static final String PROTECTED_RESOURCE_URL = - "https://preview.account.thethingsnetwork.org/api/v2/applications"; + private static final String PROTECTED_RESOURCE_URL + = "https://preview.account.thethingsnetwork.org/api/v2/applications"; private TheThingsNetworkV2PreviewExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your client id and secret final String clientId = "your_client_id"; @@ -31,7 +32,6 @@ public static void main(String... args) throws IOException, InterruptedException final OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) - .state(secretState) .callback(redirectURI) .build(TheThingsNetworkV2PreviewApi.instance()); final Scanner in = new Scanner(System.in, "UTF-8"); @@ -41,7 +41,7 @@ public static void main(String... args) throws IOException, InterruptedException // Obtain the Authorization URL System.out.println("Fetching the Authorization URL..."); - final String authorizationUrl = service.getAuthorizationUrl(); + 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); @@ -66,8 +66,7 @@ public static void main(String... args) throws IOException, InterruptedException System.out.println("Trading the Request Token for an Access Token..."); final OAuth2AccessToken accessToken = service.getAccessToken(code); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + System.out.println("(The raw response looks like this: " + accessToken.getRawResponse() + "')"); System.out.println(); // Now let's go and ask for a protected resource! @@ -76,19 +75,20 @@ public static void main(String... args) throws IOException, InterruptedException service.signRequest(accessToken, request); request.addHeader("Accept", "application/json"); - final Response response = service.execute(request); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); + try (Response response = service.execute(request)) { + System.out.println("Got it! Lets see what we found..."); + System.out.println(); + System.out.println(response.getCode()); - if (response.getCode() == 401) { - System.out.println("Not authorised: "+response.getBody()); - } else { - System.out.println("You should see a JSON array of your registered applications:"); - System.out.println(response.getBody()); + if (response.getCode() == 401) { + System.out.println("Not authorised: " + response.getBody()); + } else { + System.out.println("You should see a JSON array of your registered applications:"); + System.out.println(response.getBody()); - System.out.println(); - System.out.println("That's it man! Go and build something awesome with ScribeJava! :)"); + System.out.println(); + System.out.println("That's it man! Go and build something awesome with ScribeJava! :)"); + } } } } diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TrelloExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TrelloExample.java index dea9fc659..737bd768e 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TrelloExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TrelloExample.java @@ -12,7 +12,7 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class TrelloExample { +public class TrelloExample { private static final String API_KEY = "your_api_key"; private static final String API_SECRET = "your_api_secret"; @@ -21,6 +21,7 @@ public final class TrelloExample { private TrelloExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { final OAuth10aService service = new ServiceBuilder(API_KEY) .apiSecret(API_SECRET) @@ -43,23 +44,22 @@ public static void main(String... args) throws IOException, InterruptedException final String oauthVerifier = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token + // Trade the Request Token and Verifier for the Access Token System.out.println("Trading the Request Token for an Access Token..."); final OAuth1AccessToken accessToken = service.getAccessToken(requestToken, oauthVerifier); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final Response response = service.execute(request); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getBody()); - + try (Response response = service.execute(request)) { + System.out.println("Got it! Lets see what we found..."); + System.out.println(); + 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/TumblrExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TumblrExample.java index ec21d3bfb..063b8b9b2 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TumblrExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TumblrExample.java @@ -12,13 +12,14 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class TumblrExample { +public class TumblrExample { private static final String PROTECTED_RESOURCE_URL = "http://api.tumblr.com/v2/user/info"; private TumblrExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { final OAuth10aService service = new ServiceBuilder("MY_CONSUMER_KEY") .apiSecret("MY_CONSUMER_SECRET") @@ -43,23 +44,22 @@ public static void main(String... args) throws IOException, InterruptedException final String oauthVerifier = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token + // Trade the Request Token and Verifier for the Access Token System.out.println("Trading the Request Token for an Access Token..."); final OAuth1AccessToken accessToken = service.getAccessToken(requestToken, oauthVerifier); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final Response response = service.execute(request); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getBody()); - + try (Response response = service.execute(request)) { + System.out.println("Got it! Lets see what we found..."); + System.out.println(); + System.out.println(response.getBody()); + } System.out.println(); System.out.println("Thats it man! Go and build something awesome with Scribe! :)"); } diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TutByExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TutByExample.java index daa5f0122..c6aaf7eab 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TutByExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TutByExample.java @@ -13,7 +13,7 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class TutByExample { +public class TutByExample { private static final String NETWORK_NAME = "Tut.by"; private static final String PROTECTED_RESOURCE_URL = "http://profile.tut.by/getInfo"; @@ -21,6 +21,7 @@ public final class TutByExample { private TutByExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your client id and secret final String clientId = "your client id"; @@ -44,24 +45,22 @@ public static void main(String... args) throws IOException, InterruptedException final String code = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token - System.out.println("Trading the Request Token for an Access Token..."); + 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("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final 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()); - + 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! :)"); } diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TwitterExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TwitterExample.java index 620eb7820..748fa9034 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TwitterExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TwitterExample.java @@ -12,13 +12,14 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class TwitterExample { +public class TwitterExample { private static final String PROTECTED_RESOURCE_URL = "https://api.twitter.com/1.1/account/verify_credentials.json"; private TwitterExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { final OAuth10aService service = new ServiceBuilder("your client id") .apiSecret("your client secret") @@ -41,23 +42,22 @@ public static void main(String... args) throws IOException, InterruptedException final String oauthVerifier = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token + // Trade the Request Token and Verifier for the Access Token System.out.println("Trading the Request Token for an Access Token..."); final OAuth1AccessToken accessToken = service.getAccessToken(requestToken, oauthVerifier); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final Response response = service.execute(request); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getBody()); - + try (Response response = service.execute(request)) { + System.out.println("Got it! Lets see what we found..."); + System.out.println(); + System.out.println(response.getBody()); + } System.out.println(); System.out.println("That's it man! Go and build something awesome with ScribeJava! :)"); } diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SohuWeiboExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/UcozExample.java similarity index 51% rename from scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SohuWeiboExample.java rename to scribejava-apis/src/test/java/com/github/scribejava/apis/examples/UcozExample.java index 0cb93b91a..5760bb36a 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SohuWeiboExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/UcozExample.java @@ -1,72 +1,59 @@ package com.github.scribejava.apis.examples; -import java.util.Scanner; +import com.github.scribejava.apis.UcozApi; import com.github.scribejava.core.builder.ServiceBuilder; -import com.github.scribejava.apis.SohuWeiboApi; import com.github.scribejava.core.model.OAuth1AccessToken; import com.github.scribejava.core.model.OAuth1RequestToken; 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.OAuth10aService; + import java.io.IOException; +import java.util.Scanner; import java.util.concurrent.ExecutionException; -public final class SohuWeiboExample { +public class UcozExample { - private static final String NETWORK_NAME = "SohuWeibo"; - private static final String PROTECTED_RESOURCE_URL = "http://api.t.sohu.com/account/verify_credentials.json"; + private static final String PROTECTED_RESOURCE_URL = "http://artmurka.com/uapi/shop/request?page=categories"; - private SohuWeiboExample() { + private UcozExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { - // Replace these with your own api key and secret - final String apiKey = "your_key"; - final String apiSecret = "your_secret"; - final OAuth10aService service = new ServiceBuilder(apiKey) - .apiSecret(apiSecret) - .build(SohuWeiboApi.instance()); + final OAuth10aService service = new ServiceBuilder("your_api_key") + .apiSecret("your_api_secret") + .debug() + .build(UcozApi.instance()); final Scanner in = new Scanner(System.in); - - System.out.println("=== " + NETWORK_NAME + "'s OAuth Workflow ==="); - System.out.println(); - - // Grab a request token. - System.out.println("Fetching request token."); final OAuth1RequestToken requestToken = service.getRequestToken(); - System.out.println("Got it ... "); - System.out.println(requestToken.getToken()); + System.out.println("Got the Request Token!"); + System.out.println(); - // Obtain the Authorization URL - System.out.println("Fetching the Authorization URL..."); - final String authorizationUrl = service.getAuthorizationUrl(requestToken); - 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.println(service.getAuthorizationUrl(requestToken)); + System.out.println("And paste the verifier here"); System.out.print(">>"); final String oauthVerifier = in.nextLine(); System.out.println(); - - // Trade the Request Token and Verfier for the Access Token + // Trade the Request Token and Verifier for the Access Token System.out.println("Trading the Request Token for an Access Token..."); final OAuth1AccessToken accessToken = service.getAccessToken(requestToken, oauthVerifier); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final 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()); - + 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! :)"); } diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/ViadeoExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/ViadeoExample.java index 09dcde65a..39dbcff78 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/ViadeoExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/ViadeoExample.java @@ -11,7 +11,7 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class ViadeoExample { +public class ViadeoExample { private static final String NETWORK_NAME = "Viadeo"; private static final String PROTECTED_RESOURCE_URL = "https://api.viadeo.com/me?user_detail=full"; @@ -19,6 +19,7 @@ public final class ViadeoExample { private ViadeoExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your own api key and secret final String apiKey = "your_app_id"; @@ -43,24 +44,22 @@ public static void main(String... args) throws IOException, InterruptedException final String code = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token - System.out.println("Trading the Request Token for an Access Token..."); + 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("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final 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()); - + 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! :)"); diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteAsyncApacheExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteAsyncApacheExample.java new file mode 100644 index 000000000..392348b2b --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteAsyncApacheExample.java @@ -0,0 +1,87 @@ +package com.github.scribejava.apis.examples; + +import java.util.Random; +import java.util.Scanner; +import java.util.concurrent.ExecutionException; +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; +import com.github.scribejava.core.model.Response; +import com.github.scribejava.core.model.Verb; +import com.github.scribejava.core.oauth.OAuth20Service; +import com.github.scribejava.httpclient.apache.ApacheHttpClientConfig; +import java.io.IOException; + +public class VkontakteAsyncApacheExample { + + 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 VkontakteAsyncApacheExample() { + } + + @SuppressWarnings("PMD.SystemPrintln") + public static void main(String... args) throws InterruptedException, ExecutionException, IOException { + // Replace these with your client id and secret + final String clientId = "your client id"; + final String clientSecret = "your client secret"; + final String secretState = "secret" + new Random().nextInt(999_999); + + try ( OAuth20Service service = new ServiceBuilder(clientId) + .apiSecret(clientSecret) + .callback("http://www.example.com/oauth_callback/") + .httpClientConfig(ApacheHttpClientConfig.defaultConfig()) + .build(VkontakteApi.instance())) { + final Scanner in = new Scanner(System.in, "UTF-8"); + + System.out.println("=== " + NETWORK_NAME + "'s Async 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.getAccessTokenAsync(code).get(); + 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! :)"); + } + } +} 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 78% 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 ae8879790..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,14 +14,16 @@ import com.github.scribejava.core.oauth.OAuth20Service; import java.io.IOException; -public final class FacebookAsyncNingExample { +public class VkontakteAsyncNingExample { - private static final String NETWORK_NAME = "Facebook"; - private static final String PROTECTED_RESOURCE_URL = "https://graph.facebook.com/v2.8/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") public static void main(String... args) throws InterruptedException, ExecutionException, IOException { // Replace these with your client id and secret final String clientId = "your client id"; @@ -35,12 +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) - .state(secretState) .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 ==="); @@ -48,7 +49,7 @@ public static void main(String... args) throws InterruptedException, ExecutionEx // Obtain the Authorization URL System.out.println("Fetching the Authorization URL..."); - final String authorizationUrl = service.getAuthorizationUrl(); + 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); @@ -70,24 +71,23 @@ public static void main(String... args) throws InterruptedException, ExecutionEx System.out.println(); } - // Trade the Request Token and Verfier for the Access Token - System.out.println("Trading the Request Token for an Access Token..."); + System.out.println("Trading the Authorization Code for an Access Token..."); final OAuth2AccessToken accessToken = service.getAccessTokenAsync(code).get(); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final 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()); - + 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! :)"); } 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 new file mode 100644 index 000000000..3e4d2d363 --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteClientCredentialsGrantExample.java @@ -0,0 +1,38 @@ +package com.github.scribejava.apis.examples; + +import com.github.scribejava.core.builder.ServiceBuilder; +import com.github.scribejava.apis.VkontakteApi; +import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.oauth.OAuth20Service; +import java.io.IOException; +import java.util.concurrent.ExecutionException; + +public class VkontakteClientCredentialsGrantExample { + + private static final String NETWORK_NAME = "vk.com>"; + + private VkontakteClientCredentialsGrantExample() { + } + + @SuppressWarnings("PMD.SystemPrintln") + public static void main(String... args) throws IOException, InterruptedException, ExecutionException { + // Replace these with your client id and secret + final String clientId = "your client id"; + final String clientSecret = "your client secret"; + final OAuth20Service service = new ServiceBuilder(clientId) + .apiSecret(clientSecret) + .defaultScope("wall,offline") // replace with desired scope + .callback("http://your.site.com/callback") + .build(VkontakteApi.instance()); + + System.out.println("=== " + NETWORK_NAME + "'s OAuth Workflow ==="); + System.out.println(); + final OAuth2AccessToken accessToken = service.getAccessTokenClientCredentialsGrant(); + + System.out.println("Got the Access Token!"); + System.out.println(accessToken.getRawResponse()); + 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/VkontakteExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteExample.java index 240f78e7c..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 @@ -3,29 +3,33 @@ import java.util.Scanner; import com.github.scribejava.core.builder.ServiceBuilder; import com.github.scribejava.apis.VkontakteApi; +import com.github.scribejava.apis.vk.VKOAuth2AccessToken; 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.AccessTokenRequestParams; import com.github.scribejava.core.oauth.OAuth20Service; import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class VkontakteExample { +public class VkontakteExample { - private static final String NETWORK_NAME = "Vkontakte.ru"; - private static final String PROTECTED_RESOURCE_URL = "https://api.vk.com/method/users.get"; + 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 VkontakteExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your client id and secret final String clientId = "your client id"; final String clientSecret = "your client secret"; final OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) - .scope("wall,offline") // replace with desired scope + .defaultScope("wall,offline") // replace with desired scope .callback("http://your.site.com/callback") .build(VkontakteApi.instance()); final Scanner in = new Scanner(System.in); @@ -35,7 +39,10 @@ public static void main(String... args) throws IOException, InterruptedException // Obtain the Authorization URL System.out.println("Fetching the Authorization URL..."); - final String authorizationUrl = service.getAuthorizationUrl(); + final String customScope = "wall,offline,email"; + final String authorizationUrl = service.createAuthorizationUrlBuilder() + .scope(customScope) + .build(); System.out.println("Got the Authorization URL!"); System.out.println("Now go and authorize ScribeJava here:"); System.out.println(authorizationUrl); @@ -44,24 +51,27 @@ public static void main(String... args) throws IOException, InterruptedException final String code = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token - System.out.println("Trading the Request Token for an Access Token..."); - final OAuth2AccessToken accessToken = service.getAccessToken(code); + System.out.println("Trading the Authorization Code for an Access Token..."); + final OAuth2AccessToken accessToken = service.getAccessToken(AccessTokenRequestParams.create(code) + .scope(customScope)); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + System.out.println("(The raw response looks like this: " + accessToken.getRawResponse() + "')"); + if (accessToken instanceof VKOAuth2AccessToken) { + System.out.println("it's a VKOAuth2AccessToken, it has email field = '" + + ((VKOAuth2AccessToken) accessToken).getEmail() + "'."); + } 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); - final 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()); - + 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! :)"); } 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 d16b714d8..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 @@ -14,14 +14,16 @@ import org.asynchttpclient.DefaultAsyncHttpClient; import org.asynchttpclient.DefaultAsyncHttpClientConfig; -public final class VkontakteExternalHttpExample { +public class VkontakteExternalHttpExample { - private static final String NETWORK_NAME = "Vkontakte.ru"; - private static final String PROTECTED_RESOURCE_URL = "https://api.vk.com/method/users.get"; + 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 VkontakteExternalHttpExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { // Replace these with your client id and secret final String clientId = "your client id"; @@ -35,14 +37,14 @@ 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); final OAuth20Service service = new ServiceBuilder(clientId) .httpClient(wrappedAHCHttpClient) .apiSecret(clientSecret) - .scope("wall,offline") // replace with desired scope + .defaultScope("wall,offline") // replace with desired scope .callback("http://your.site.com/callback") .build(VkontakteApi.instance()); final Scanner in = new Scanner(System.in); @@ -61,24 +63,23 @@ public static void main(String... args) throws IOException, InterruptedException final String code = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token - System.out.println("Trading the Request Token for an Access Token..."); + 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("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final 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()); - + 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! :)"); } diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/WunderlistExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/WunderlistExample.java new file mode 100644 index 000000000..5d3ffd562 --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/WunderlistExample.java @@ -0,0 +1,73 @@ +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.WunderlistAPI; +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 WunderlistExample { + + private static final String NETWORK_NAME = "Wunderlist"; + private static final String PROTECTED_RESOURCE_URL = "https://a.wunderlist.com/api/v1/user"; + + private WunderlistExample() { + } + + @SuppressWarnings("PMD.SystemPrintln") + public static void main(String... args) throws IOException, InterruptedException, ExecutionException { + // Replace these with your own values + final String apiKey = "apiKey"; + final String apiSecret = "apiSecret"; + final String callbackUrl = "http://example.com/callback"; + final String secretState = "security_token" + new Random().nextInt(999_999); + + final OAuth20Service service = new ServiceBuilder(apiKey) + .apiSecret(apiSecret) + .callback(callbackUrl) + .build(WunderlistAPI.instance()); + + final String code; + try (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(">>"); + code = in.nextLine(); + } + System.out.println(); + + // Trade the Request Token and Verifier for the Access Token + System.out.println("Trading the Request Token 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! :)"); + } +} diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/XeroExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/XeroExample.java new file mode 100644 index 000000000..d66334e40 --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/XeroExample.java @@ -0,0 +1,110 @@ +package com.github.scribejava.apis.examples; + +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Scanner; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.scribejava.apis.XeroApi20; +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.concurrent.ExecutionException; + +public class XeroExample { + + private static final String NETWORK_NAME = "Xero"; + private static final String PROTECTED_RESOURCE_URL = "https://api.xero.com/connections"; + private static final String PROTECTED_ORGANISATION_URL = "https://api.xero.com/api.xro/2.0/Organisation"; + + private XeroExample() { + } + + @SuppressWarnings("PMD.SystemPrintln") + public static void main(String... args) throws IOException, InterruptedException, ExecutionException { + // Replace these with your client id and secret + final String clientId = "your client id"; + final String clientSecret = "your client secret"; + final String callback = "your callback url"; + + final String secretState = "secret" + new Random().nextInt(999_999); + final OAuth20Service service = new ServiceBuilder(clientId) + .apiSecret(clientSecret) + // replace with desired scope + .defaultScope("openid email profile offline_access accounting.settings accounting.transactions") + .callback(callback) + .build(XeroApi20.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(); + + //First GET the Xero Tenant ID + System.out.println("Getting Xero tenant id..."); + final OAuthRequest requestConn = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); + requestConn.addHeader("Accept", "application/json"); + service.signRequest(accessToken.getAccessToken(), requestConn); + final Response connResp = service.execute(requestConn); + + final ObjectMapper objectMapper = new ObjectMapper(); + final List> tenantList = objectMapper.readValue(connResp.getBody(), + objectMapper.getTypeFactory().constructCollectionType(List.class, Map.class)); + + System.out.println(); + System.out.println(connResp.getCode()); + System.out.println(connResp.getBody()); + System.out.println(); + System.out.println("Your Xero tenant id is ...." + tenantList.get(0).get("tenantId")); + System.out.println(); + + // GET protected Resource + System.out.println("Now we're going to access a protected resource..."); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_ORGANISATION_URL); + request.addHeader("xero-tenant-id", tenantList.get(0).get("tenantId")); + service.signRequest(accessToken.getAccessToken(), request); + final Response response = service.execute(request); + + // Now let's go and ask for a protected resource! + 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/XingExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/XingExample.java index 9929c6152..ebf946db5 100755 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/XingExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/XingExample.java @@ -12,13 +12,14 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class XingExample { +public class XingExample { private static final String PROTECTED_RESOURCE_URL = "https://api.xing.com/v1/users/me"; private XingExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { final OAuth10aService service = new ServiceBuilder("your client id") .apiSecret("your client secret") @@ -41,23 +42,22 @@ public static void main(String... args) throws IOException, InterruptedException final String oauthVerifier = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token + // Trade the Request Token and Verifier for the Access Token System.out.println("Trading the Request Token for an Access Token..."); final OAuth1AccessToken accessToken = service.getAccessToken(requestToken, oauthVerifier); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final Response response = service.execute(request); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getBody()); - + try (Response response = service.execute(request)) { + System.out.println("Got it! Lets see what we found..."); + System.out.println(); + 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/Yahoo20Example.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Yahoo20Example.java new file mode 100644 index 000000000..49b655462 --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Yahoo20Example.java @@ -0,0 +1,79 @@ +package com.github.scribejava.apis.examples; + +import com.github.scribejava.apis.YahooApi20; +import com.github.scribejava.core.builder.ServiceBuilder; +import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.model.OAuthConstants; +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.Scanner; +import java.util.concurrent.ExecutionException; + +/** + * Flow as documented at https://developer.yahoo.com/oauth2/guide/flows_authcode + *
    + *
  1. Create an application at https://developer.yahoo.com/apps/create/
  2. + *
  3. Make sure application has permission to API resource (Profiles, Fantasy Sports, etc)
  4. + *
  5. get Client ID and Client Secret after registering your app
  6. + *
+ */ +public class Yahoo20Example { + + private static final String PROTECTED_RESOURCE_URL + = "https://fantasysports.yahooapis.com/fantasy/v2/users;use_login=1/games/teams"; + + private Yahoo20Example() { + } + + @SuppressWarnings("PMD.SystemPrintln") + public static void main(String... args) throws IOException, InterruptedException, ExecutionException { + // Add your personal information here + final String clientId = "ADD CLIENT ID HERE!!!!"; + final String clientSecret = "ADD SECRET HERE!!!!"; + + final OAuth20Service service = new ServiceBuilder(clientId) + .apiSecret(clientSecret) + .callback(OAuthConstants.OOB) + .build(YahooApi20.instance()); + final Scanner in = new Scanner(System.in); + + System.out.println("=== Yahoo's OAuth Workflow ==="); + System.out.println(); + + // Obtain the Request Token + System.out.println("Fetching the Request Token..."); + System.out.println("Got the Request Token!"); + System.out.println(); + + System.out.println("Now go and authorize ScribeJava here:"); + System.out.println(service.getAuthorizationUrl()); + System.out.println("And paste the verifier here"); + System.out.print(">>"); + final String oauthVerifier = in.nextLine(); + System.out.println(); + + System.out.println("Trading the Authorization Code for an Access Token..."); + final OAuth2AccessToken accessToken = service.getAccessToken(oauthVerifier); + 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! :)"); + + } +} diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/YahooExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/YahooExample.java index 581600a3b..acaa42aea 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/YahooExample.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/YahooExample.java @@ -12,7 +12,7 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; -public final class YahooExample { +public class YahooExample { private static final String PROTECTED_RESOURCE_URL = "http://social.yahooapis.com/v1/user/A6ROU63MXWDCW3Y5MGCYWVHDJI/profile/status?format=json"; @@ -20,6 +20,7 @@ public final class YahooExample { private YahooExample() { } + @SuppressWarnings("PMD.SystemPrintln") public static void main(String... args) throws IOException, InterruptedException, ExecutionException { final OAuth10aService service = new ServiceBuilder("your client id") .apiSecret("your client secret") @@ -42,24 +43,23 @@ public static void main(String... args) throws IOException, InterruptedException final String oauthVerifier = in.nextLine(); System.out.println(); - // Trade the Request Token and Verfier for the Access Token + // Trade the Request Token and Verifier for the Access Token System.out.println("Trading the Request Token for an Access Token..."); final OAuth1AccessToken accessToken = service.getAccessToken(requestToken, oauthVerifier); System.out.println("Got the Access Token!"); - System.out.println("(if your curious it looks like this: " + accessToken - + ", 'rawResponse'='" + accessToken.getRawResponse() + "')"); + 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); - final 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()); - + 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! :)"); 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 new file mode 100644 index 000000000..240f08b81 --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/facebook/FacebookAccessTokenJsonExtractorTest.java @@ -0,0 +1,43 @@ +package com.github.scribejava.apis.facebook; + +import com.github.scribejava.core.model.Response; +import java.io.IOException; +import java.util.Collections; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import org.junit.Test; +import org.junit.function.ThrowingRunnable; + +public class FacebookAccessTokenJsonExtractorTest { + + private final FacebookAccessTokenJsonExtractor extractor = FacebookAccessTokenJsonExtractor.instance(); + + @Test + public void shouldThrowExceptionIfResponseIsError() throws IOException { + final String body = "{\"error\":" + + "{\"message\":\"This authorization code has been used.\"," + + "\"type\":\"OAuthException\"," + + "\"code\":100," + + "\"fbtrace_id\":\"DtxvtGRaxbB\"}}"; + try (Response response = error(body)) { + + 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.getResponse().getBody()); + } + } + + private static Response error(String body) { + return new Response(400, /* message */ null, /* headers */ Collections.emptyMap(), body); + } +} 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 new file mode 100644 index 000000000..836a0d6ba --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/fitbit/FitBitJsonTokenExtractorTest.java @@ -0,0 +1,38 @@ +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; + +import org.junit.Test; + +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertEquals; +import org.junit.function.ThrowingRunnable; + +public class FitBitJsonTokenExtractorTest { + + private static final String ERROR_DESCRIPTION = "Authorization code invalid: " + + "cbb1c11b23209011e89be71201fa6381464dc0af " + + "Visit https://dev.fitbit.com/docs/oauth2 for more information " + + "on the Fitbit Web API authorization process."; + private static final String ERROR_JSON = "{\"errors\":[{\"errorType\":\"invalid_grant\",\"message\":\"" + + ERROR_DESCRIPTION + "\"}],\"success\":false}"; + + @Test + public void testErrorExtraction() throws IOException { + + final FitBitJsonTokenExtractor extractor = new FitBitJsonTokenExtractor(); + final OAuth2AccessTokenErrorResponse thrown = assertThrows(OAuth2AccessTokenErrorResponse.class, + new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.generateError(new Response(403, null, null, ERROR_JSON)); + } + }); + assertSame(OAuth2Error.INVALID_GRANT, thrown.getError()); + assertEquals(ERROR_DESCRIPTION, thrown.getErrorDescription()); + } +} diff --git a/scribejava-apis/src/test/java/com/github/scribejava/apis/service/OdnoklassnikiServiceTest.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/odnoklassniki/OdnoklassnikiServiceTest.java similarity index 94% rename from scribejava-apis/src/test/java/com/github/scribejava/apis/service/OdnoklassnikiServiceTest.java rename to scribejava-apis/src/test/java/com/github/scribejava/apis/odnoklassniki/OdnoklassnikiServiceTest.java index 0dded23fb..f4bfbccac 100644 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/service/OdnoklassnikiServiceTest.java +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/odnoklassniki/OdnoklassnikiServiceTest.java @@ -1,4 +1,4 @@ -package com.github.scribejava.apis.service; +package com.github.scribejava.apis.odnoklassniki; import com.github.scribejava.apis.OdnoklassnikiApi; import com.github.scribejava.core.builder.ServiceBuilder; @@ -19,7 +19,7 @@ public class OdnoklassnikiServiceTest { private final OAuth20Service service = new ServiceBuilder("0000000000") .apiSecret("CCCCCCCCCCCCCCCCCCCCCCCC") - .scope("VALUABLE_ACCESS") + .defaultScope("VALUABLE_ACCESS") .callback("http://your.site.com/callback") .build(OdnoklassnikiApi.instance()); diff --git a/scribejava-core/pom.xml b/scribejava-core/pom.xml index c0721530c..d5fbfc488 100644 --- a/scribejava-core/pom.xml +++ b/scribejava-core/pom.xml @@ -5,15 +5,41 @@ com.github.scribejava scribejava - 4.1.3-SNAPSHOT + 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 + + + @@ -23,6 +49,13 @@ org.apache.maven.plugins maven-jar-plugin + + + + test-jar + + + 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 a3ff17365..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 @@ -1,10 +1,11 @@ package com.github.scribejava.core.builder; -import com.github.scribejava.core.builder.api.BaseApi; +import com.github.scribejava.core.builder.api.DefaultApi10a; +import com.github.scribejava.core.builder.api.DefaultApi20; import com.github.scribejava.core.httpclient.HttpClient; import com.github.scribejava.core.httpclient.HttpClientConfig; -import com.github.scribejava.core.model.OAuthConfig; -import com.github.scribejava.core.model.OAuthConstants; +import com.github.scribejava.core.oauth.OAuth10aService; +import com.github.scribejava.core.oauth.OAuth20Service; import com.github.scribejava.core.oauth.OAuthService; import com.github.scribejava.core.utils.Preconditions; @@ -13,13 +14,12 @@ /** * Implementation of the Builder pattern, with a fluent interface that creates a {@link OAuthService} */ -public class ServiceBuilder { +public class ServiceBuilder implements ServiceBuilderOAuth10a, ServiceBuilderOAuth20 { - private String callback = OAuthConstants.OUT_OF_BAND; + private String callback; private String apiKey; private String apiSecret; private String scope; - private String state; private OutputStream debugStream; private String responseType = "code"; private String userAgent; @@ -31,114 +31,100 @@ public ServiceBuilder(String apiKey) { apiKey(apiKey); } - /** - * Adds an OAuth callback url - * - * @param callback callback url. Must be a valid url or 'oob' for out of band OAuth - * @return the {@link ServiceBuilder} instance for method chaining - */ + @Override public ServiceBuilder callback(String callback) { - Preconditions.checkNotNull(callback, "Callback can't be null"); this.callback = callback; return this; } - /** - * Configures the api key - * - * @param apiKey The api key for your application - * @return the {@link ServiceBuilder} instance for method chaining - */ + @Override public final ServiceBuilder apiKey(String apiKey) { Preconditions.checkEmptyString(apiKey, "Invalid Api key"); this.apiKey = apiKey; return this; } - /** - * Configures the api secret - * - * @param apiSecret The api secret for your application - * @return the {@link ServiceBuilder} instance for method chaining - */ + @Override public ServiceBuilder apiSecret(String apiSecret) { Preconditions.checkEmptyString(apiSecret, "Invalid Api secret"); this.apiSecret = apiSecret; return this; } - /** - * Configures the OAuth scope. This is only necessary in some APIs (like Google's). - * - * @param scope The OAuth scope - * @return the {@link ServiceBuilder} instance for method chaining - */ - public ServiceBuilder scope(String scope) { + @Override + public ServiceBuilder apiSecretIsEmptyStringUnsafe() { + apiSecret = ""; + return this; + } + + private ServiceBuilder setScope(String scope) { Preconditions.checkEmptyString(scope, "Invalid OAuth scope"); this.scope = scope; return this; } - /** - * Configures the anti forgery session state. This is available in some APIs (like Google's). - * - * @param state The OAuth state - * @return the {@link ServiceBuilder} instance for method chaining - */ - public ServiceBuilder state(String state) { - Preconditions.checkEmptyString(state, "Invalid OAuth state"); - this.state = state; - return this; + @Override + 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); + } + + @Override public ServiceBuilder debugStream(OutputStream debugStream) { Preconditions.checkNotNull(debugStream, "debug stream can't be null"); this.debugStream = debugStream; return this; } - public ServiceBuilder responseType(String responseType) { + @Override + public ServiceBuilderOAuth20 responseType(String responseType) { Preconditions.checkEmptyString(responseType, "Invalid OAuth responseType"); this.responseType = responseType; return this; } + @Override public ServiceBuilder httpClientConfig(HttpClientConfig httpClientConfig) { Preconditions.checkNotNull(httpClientConfig, "httpClientConfig can't be null"); this.httpClientConfig = httpClientConfig; return this; } - /** - * takes precedence over httpClientConfig - * - * @param httpClient externally created HTTP client - * @return the {@link ServiceBuilder} instance for method chaining - */ + @Override public ServiceBuilder httpClient(HttpClient httpClient) { this.httpClient = httpClient; return this; } + @Override public ServiceBuilder userAgent(String userAgent) { this.userAgent = userAgent; return this; } + @Override public ServiceBuilder debug() { - debugStream(System.out); - return this; + return debugStream(System.out); + } + + @Override + public OAuth10aService build(DefaultApi10a api) { + return api.createService(apiKey, apiSecret, callback, scope, debugStream, userAgent, httpClientConfig, + httpClient); } - /** - * Returns the fully configured {@link S} - * - * @param OAuthService implementation (OAuth1/OAuth2/any API specific) - * @param api will build Service for this API - * @return fully configured {@link S} - */ - public > S build(BaseApi api) { - return api.createService(new OAuthConfig(apiKey, apiSecret, callback, scope, debugStream, state, responseType, - userAgent, httpClientConfig, httpClient)); + @Override + public OAuth20Service build(DefaultApi20 api) { + return api.createService(apiKey, apiSecret, callback, scope, responseType, debugStream, userAgent, + httpClientConfig, httpClient); } } 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 new file mode 100644 index 000000000..fd71c49d8 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/builder/ServiceBuilderCommon.java @@ -0,0 +1,63 @@ +package com.github.scribejava.core.builder; + +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; +import com.github.scribejava.core.oauth.OAuthService; +import java.io.OutputStream; + +/** + * Implementation of the Builder pattern, with a fluent interface that creates a {@link OAuthService} + */ +public interface ServiceBuilderCommon { + + /** + * Adds an OAuth callback url + * + * @param callback callback url. Must be a valid url or 'oob' + * ({@link com.github.scribejava.core.model.OAuthConstants#OOB} for out of band OAuth + * @return the {@link ServiceBuilder} instance for method chaining + */ + ServiceBuilderCommon callback(String callback); + + /** + * Configures the api key + * + * @param apiKey The api key for your application + * @return the {@link ServiceBuilder} instance for method chaining + */ + ServiceBuilderCommon apiKey(String apiKey); + + /** + * Configures the api secret + * + * @param apiSecret The api secret for your application + * @return the {@link ServiceBuilder} instance for method chaining + */ + 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); + + /** + * takes precedence over httpClientConfig + * + * @param httpClient externally created HTTP client + * @return the {@link ServiceBuilder} instance for method chaining + */ + ServiceBuilderCommon httpClient(HttpClient httpClient); + + ServiceBuilderCommon userAgent(String userAgent); + + ServiceBuilderCommon debugStream(OutputStream debugStream); + + ServiceBuilderCommon debug(); +} 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 new file mode 100644 index 000000000..8c7027cca --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/builder/ServiceBuilderOAuth10a.java @@ -0,0 +1,47 @@ +package com.github.scribejava.core.builder; + +import com.github.scribejava.core.builder.api.DefaultApi10a; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; +import com.github.scribejava.core.oauth.OAuth10aService; +import java.io.OutputStream; + +public interface ServiceBuilderOAuth10a extends ServiceBuilderCommon { + + @Override + ServiceBuilderOAuth10a callback(String callback); + + @Override + ServiceBuilderOAuth10a apiKey(String apiKey); + + @Override + ServiceBuilderOAuth10a apiSecret(String apiSecret); + + @Override + ServiceBuilderOAuth10a apiSecretIsEmptyStringUnsafe(); + + @Override + ServiceBuilderOAuth10a httpClientConfig(HttpClientConfig httpClientConfig); + + @Override + ServiceBuilderOAuth10a httpClient(HttpClient httpClient); + + @Override + ServiceBuilderOAuth10a userAgent(String userAgent); + + @Override + ServiceBuilderOAuth10a debugStream(OutputStream debugStream); + + @Override + ServiceBuilderOAuth10a debug(); + + /** + * Configures the OAuth 1.0a scope. This is only necessary in some APIs + * + * @param scope The OAuth scope + * @return the {@link ServiceBuilder} instance for method chaining + */ + ServiceBuilderOAuth10a withScope(String scope); + + OAuth10aService build(DefaultApi10a api); +} 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 new file mode 100644 index 000000000..bcf119db9 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/builder/ServiceBuilderOAuth20.java @@ -0,0 +1,57 @@ +package com.github.scribejava.core.builder; + +import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; +import com.github.scribejava.core.oauth.OAuth20Service; +import java.io.OutputStream; + +public interface ServiceBuilderOAuth20 extends ServiceBuilderCommon { + + @Override + ServiceBuilderOAuth20 callback(String callback); + + @Override + ServiceBuilderOAuth20 apiKey(String apiKey); + + @Override + ServiceBuilderOAuth20 apiSecret(String apiSecret); + + @Override + ServiceBuilderOAuth20 apiSecretIsEmptyStringUnsafe(); + + @Override + ServiceBuilderOAuth20 httpClientConfig(HttpClientConfig httpClientConfig); + + @Override + ServiceBuilderOAuth20 httpClient(HttpClient httpClient); + + @Override + ServiceBuilderOAuth20 userAgent(String userAgent); + + @Override + ServiceBuilderOAuth20 debugStream(OutputStream debugStream); + + @Override + ServiceBuilderOAuth20 debug(); + + ServiceBuilderOAuth20 responseType(String responseType); + + /** + * Configures the default OAuth 2.0 scope.
+ * + * You can request any uniq scope per each access token request using + * {@link com.github.scribejava.core.oauth.AuthorizationUrlBuilder#scope(java.lang.String) }.

+ * + * In case you're requesting always the same scope,
+ * you can just set it here and do not provide scope param nowhere more. + * + * @param defaultScope The OAuth scope, used as deafult + * @return the {@link ServiceBuilder} instance for method chaining + */ + ServiceBuilderOAuth20 defaultScope(String defaultScope); + + ServiceBuilderOAuth20 defaultScope(ScopeBuilder scopeBuilder); + + OAuth20Service build(DefaultApi20 api); +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/builder/api/BaseApi.java b/scribejava-core/src/main/java/com/github/scribejava/core/builder/api/BaseApi.java deleted file mode 100644 index 7f6fbdd4f..000000000 --- a/scribejava-core/src/main/java/com/github/scribejava/core/builder/api/BaseApi.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.github.scribejava.core.builder.api; - -import com.github.scribejava.core.model.OAuthConfig; -import com.github.scribejava.core.oauth.OAuthService; - -public interface BaseApi> { - - T createService(OAuthConfig config); -} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/builder/api/DefaultApi10a.java b/scribejava-core/src/main/java/com/github/scribejava/core/builder/api/DefaultApi10a.java index b7167a86e..2ea470110 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/builder/api/DefaultApi10a.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/builder/api/DefaultApi10a.java @@ -6,7 +6,6 @@ import com.github.scribejava.core.extractors.HeaderExtractorImpl; import com.github.scribejava.core.extractors.OAuth1AccessTokenExtractor; import com.github.scribejava.core.extractors.OAuth1RequestTokenExtractor; -import com.github.scribejava.core.model.OAuthConfig; import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth10aService; import com.github.scribejava.core.services.HMACSha1SignatureService; @@ -14,8 +13,13 @@ import com.github.scribejava.core.services.TimestampService; import com.github.scribejava.core.services.TimestampServiceImpl; 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.OAuth1AccessToken; import com.github.scribejava.core.model.OAuth1RequestToken; +import com.github.scribejava.core.model.OAuthConstants; +import com.github.scribejava.core.model.ParameterList; +import java.io.OutputStream; /** * Default implementation of the OAuth protocol, version 1.0a @@ -30,7 +34,7 @@ * fine-tune the process. Please read the javadocs of the interfaces to get an idea of what to do. * */ -public abstract class DefaultApi10a implements BaseApi { +public abstract class DefaultApi10a { /** * Returns the access token extractor. @@ -81,7 +85,7 @@ public SignatureService getSignatureService() { * @return the signature type, choose between header, querystring, etc. Defaults to Header */ public OAuth1SignatureType getSignatureType() { - return OAuth1SignatureType.Header; + return OAuth1SignatureType.HEADER; } /** @@ -125,17 +129,24 @@ public Verb getRequestTokenVerb() { */ public abstract String getAccessTokenEndpoint(); + protected abstract String getAuthorizationBaseUrl(); + /** * Returns the URL where you should redirect your users to authenticate your application. * * @param requestToken the request token you need to authorize * @return the URL where you should redirect your users */ - public abstract String getAuthorizationUrl(OAuth1RequestToken requestToken); + public String getAuthorizationUrl(OAuth1RequestToken requestToken) { + final ParameterList parameters = new ParameterList(); + parameters.add(OAuthConstants.TOKEN, requestToken.getToken()); + return parameters.appendTo(getAuthorizationBaseUrl()); + } - @Override - public OAuth10aService createService(OAuthConfig config) { - return new OAuth10aService(this, config); + public OAuth10aService createService(String apiKey, String apiSecret, String callback, String scope, + OutputStream debugStream, String userAgent, HttpClientConfig httpClientConfig, HttpClient httpClient) { + return new OAuth10aService(this, apiKey, apiSecret, callback, scope, debugStream, userAgent, httpClientConfig, + httpClient); } /** diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/builder/api/DefaultApi20.java b/scribejava-core/src/main/java/com/github/scribejava/core/builder/api/DefaultApi20.java index 5a9e7ce44..76ef6dbce 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/builder/api/DefaultApi20.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/builder/api/DefaultApi20.java @@ -1,13 +1,21 @@ package com.github.scribejava.core.builder.api; +import com.github.scribejava.core.extractors.DeviceAuthorizationJsonExtractor; import com.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor; 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.model.OAuthConfig; import com.github.scribejava.core.model.OAuthConstants; import com.github.scribejava.core.model.ParameterList; import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth20Service; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignature; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignatureAuthorizationRequestHeaderField; +import com.github.scribejava.core.oauth2.clientauthentication.ClientAuthentication; +import com.github.scribejava.core.oauth2.clientauthentication.HttpBasicAuthenticationScheme; + +import java.io.OutputStream; import java.util.Map; /** @@ -23,7 +31,7 @@ * fine-tune the process. Please read the javadocs of the interfaces to get an idea of what to do. * */ -public abstract class DefaultApi20 implements BaseApi { +public abstract class DefaultApi20 { /** * Returns the access token extractor. @@ -54,31 +62,45 @@ public String getRefreshTokenEndpoint() { return getAccessTokenEndpoint(); } + /** + * As stated in RFC 7009 OAuth 2.0 Token Revocation + * + * @return endpoint, which allows clients to notify the authorization server that a previously obtained refresh or + * access token is no longer needed. + * @see RFC 7009 + */ + public String getRevokeTokenEndpoint() { + throw new UnsupportedOperationException( + "This API doesn't support revoking tokens or we have no info about this"); + } + protected abstract String getAuthorizationBaseUrl(); /** * Returns the URL where you should redirect your users to authenticate your application. * - * @param config OAuth 2.0 configuration param object + * @param responseType responseType + * @param apiKey apiKey * @param additionalParams any additional GET params to add to the URL + * @param callback callback + * @param state state + * @param scope scope * @return the URL where you should redirect your users */ - public String getAuthorizationUrl(OAuthConfig config, Map additionalParams) { + public String getAuthorizationUrl(String responseType, String apiKey, String callback, String scope, String state, + Map additionalParams) { final ParameterList parameters = new ParameterList(additionalParams); - parameters.add(OAuthConstants.RESPONSE_TYPE, config.getResponseType()); - parameters.add(OAuthConstants.CLIENT_ID, config.getApiKey()); + parameters.add(OAuthConstants.RESPONSE_TYPE, responseType); + parameters.add(OAuthConstants.CLIENT_ID, apiKey); - final String callback = config.getCallback(); if (callback != null) { parameters.add(OAuthConstants.REDIRECT_URI, callback); } - final String scope = config.getScope(); if (scope != null) { parameters.add(OAuthConstants.SCOPE, scope); } - final String state = config.getState(); if (state != null) { parameters.add(OAuthConstants.STATE, state); } @@ -86,12 +108,33 @@ public String getAuthorizationUrl(OAuthConfig config, Map additi return parameters.appendTo(getAuthorizationBaseUrl()); } - @Override - public OAuth20Service createService(OAuthConfig config) { - return new OAuth20Service(this, config); + public OAuth20Service createService(String apiKey, String apiSecret, String callback, String defaultScope, + String responseType, OutputStream debugStream, String userAgent, HttpClientConfig httpClientConfig, + HttpClient httpClient) { + return new OAuth20Service(this, apiKey, apiSecret, callback, defaultScope, responseType, debugStream, userAgent, + httpClientConfig, httpClient); + } + + public BearerSignature getBearerSignature() { + return BearerSignatureAuthorizationRequestHeaderField.instance(); + } + + public ClientAuthentication getClientAuthentication() { + return HttpBasicAuthenticationScheme.instance(); + } + + /** + * RFC 8628 OAuth 2.0 Device Authorization Grant + * + * @see RFC 8628 + * @return the device authorization endpoint + */ + public String getDeviceAuthorizationEndpoint() { + throw new UnsupportedOperationException( + "This API doesn't support Device Authorization Grant or we have no info about this"); } - public OAuth2SignatureType getSignatureType() { - return OAuth2SignatureType.BEARER_AUTHORIZATION_REQUEST_HEADER_FIELD; + public DeviceAuthorizationJsonExtractor getDeviceAuthorizationExtractor() { + return DeviceAuthorizationJsonExtractor.instance(); } } diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/builder/api/OAuth1SignatureType.java b/scribejava-core/src/main/java/com/github/scribejava/core/builder/api/OAuth1SignatureType.java index 7660b5826..7de68ff7b 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/builder/api/OAuth1SignatureType.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/builder/api/OAuth1SignatureType.java @@ -2,6 +2,6 @@ public enum OAuth1SignatureType { - Header, - QueryString + HEADER, + QUERY_STRING } diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/builder/api/OAuth2SignatureType.java b/scribejava-core/src/main/java/com/github/scribejava/core/builder/api/OAuth2SignatureType.java deleted file mode 100644 index a6825a757..000000000 --- a/scribejava-core/src/main/java/com/github/scribejava/core/builder/api/OAuth2SignatureType.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.github.scribejava.core.builder.api; - -import com.github.scribejava.core.model.OAuth2AccessToken; -import com.github.scribejava.core.model.OAuthConstants; -import com.github.scribejava.core.model.OAuthRequest; - -public enum OAuth2SignatureType { - /** - * https://tools.ietf.org/html/rfc6750#section-2.1 - */ - BEARER_AUTHORIZATION_REQUEST_HEADER_FIELD { - @Override - public void signRequest(OAuth2AccessToken accessToken, OAuthRequest request) { - request.addHeader("Authorization", "Bearer " + accessToken.getAccessToken()); - } - - }, - /** - * https://tools.ietf.org/html/rfc6750#section-2.3 - */ - BEARER_URI_QUERY_PARAMETER { - @Override - public void signRequest(OAuth2AccessToken accessToken, OAuthRequest request) { - request.addQuerystringParameter(OAuthConstants.ACCESS_TOKEN, accessToken.getAccessToken()); - } - - }; - - public abstract void signRequest(OAuth2AccessToken accessToken, OAuthRequest request); -} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/extractors/AbstractJsonExtractor.java b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/AbstractJsonExtractor.java new file mode 100644 index 000000000..4a592a706 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/AbstractJsonExtractor.java @@ -0,0 +1,22 @@ +package com.github.scribejava.core.extractors; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.scribejava.core.exceptions.OAuthException; + +public abstract class AbstractJsonExtractor { + + protected static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + protected static JsonNode extractRequiredParameter(JsonNode errorNode, String parameterName, String rawResponse) + throws OAuthException { + final JsonNode value = errorNode.get(parameterName); + + if (value == null) { + throw new OAuthException("Response body is incorrect. Can't extract a '" + parameterName + + "' from this: '" + rawResponse + "'", null); + } + + return value; + } +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/extractors/AbstractOAuth1JSONTokenExtractor.java b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/AbstractOAuth1JSONTokenExtractor.java new file mode 100644 index 000000000..5c42e812a --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/AbstractOAuth1JSONTokenExtractor.java @@ -0,0 +1,38 @@ +package com.github.scribejava.core.extractors; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.scribejava.core.exceptions.OAuthException; +import com.github.scribejava.core.model.OAuth1Token; +import com.github.scribejava.core.model.OAuthConstants; +import com.github.scribejava.core.model.Response; +import com.github.scribejava.core.utils.Preconditions; + +import java.io.IOException; + + +public abstract class AbstractOAuth1JSONTokenExtractor implements TokenExtractor { + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + @Override + public T extract(Response response) throws IOException { + final String rawBody = response.getBody(); + Preconditions.checkEmptyString(rawBody, + "Response body is incorrect. Can't extract a token from an empty string"); + + final JsonNode body = OBJECT_MAPPER.readTree(rawBody); + + final JsonNode token = body.get(OAuthConstants.TOKEN); + final JsonNode secret = body.get(OAuthConstants.TOKEN_SECRET); + + if (token == null || secret == null) { + throw new OAuthException("Response body is incorrect. Can't extract token and secret from this: '" + + rawBody + '\'', null); + } + + return createToken(token.asText(), secret.asText(), rawBody); + } + + protected abstract T createToken(String token, String secret, String response); +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/extractors/BaseStringExtractorImpl.java b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/BaseStringExtractorImpl.java index 879dac9ae..3547fbcac 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/extractors/BaseStringExtractorImpl.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/BaseStringExtractorImpl.java @@ -8,6 +8,7 @@ /** * Default implementation of {@link BaseStringExtractor}. Conforms to OAuth 1.0a + * https://tools.ietf.org/html/rfc5849#section-3.4.1.1 */ public class BaseStringExtractorImpl implements BaseStringExtractor { @@ -29,6 +30,11 @@ protected String getVerb(OAuthRequest request) { return request.getVerb().name(); } + /** + * https://tools.ietf.org/html/rfc5849#section-3.4.1.2 + * @param request request + * @return url + */ protected String getUrl(OAuthRequest request) { return request.getSanitizedUrl(); } 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 new file mode 100644 index 000000000..5b4fa248b --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/DeviceAuthorizationJsonExtractor.java @@ -0,0 +1,61 @@ +package com.github.scribejava.core.extractors; + +import static com.github.scribejava.core.extractors.AbstractJsonExtractor.OBJECT_MAPPER; +import static com.github.scribejava.core.extractors.AbstractJsonExtractor.extractRequiredParameter; +import com.fasterxml.jackson.databind.JsonNode; +import com.github.scribejava.core.model.DeviceAuthorization; +import java.io.IOException; +import com.github.scribejava.core.model.Response; + +public class DeviceAuthorizationJsonExtractor extends AbstractJsonExtractor { + + protected DeviceAuthorizationJsonExtractor() { + } + + private static class InstanceHolder { + + private static final DeviceAuthorizationJsonExtractor INSTANCE = new DeviceAuthorizationJsonExtractor(); + } + + public static DeviceAuthorizationJsonExtractor instance() { + return InstanceHolder.INSTANCE; + } + + public DeviceAuthorization extract(Response response) throws IOException { + if (response.getCode() != 200) { + generateError(response); + } + return createDeviceAuthorization(response.getBody()); + } + + public void generateError(Response response) throws IOException { + OAuth2AccessTokenJsonExtractor.instance().generateError(response); + } + + private DeviceAuthorization createDeviceAuthorization(String rawResponse) throws IOException { + + final JsonNode response = OBJECT_MAPPER.readTree(rawResponse); + + final DeviceAuthorization deviceAuthorization = new DeviceAuthorization( + extractRequiredParameter(response, "device_code", rawResponse).textValue(), + extractRequiredParameter(response, "user_code", rawResponse).textValue(), + extractRequiredParameter(response, getVerificationUriParamName(), rawResponse).textValue(), + extractRequiredParameter(response, "expires_in", rawResponse).intValue()); + + final JsonNode intervalSeconds = response.get("interval"); + if (intervalSeconds != null) { + deviceAuthorization.setIntervalSeconds(intervalSeconds.asInt(5)); + } + + final JsonNode verificationUriComplete = response.get("verification_uri_complete"); + if (verificationUriComplete != null) { + deviceAuthorization.setVerificationUriComplete(verificationUriComplete.asText()); + } + + return deviceAuthorization; + } + + protected String getVerificationUriParamName() { + return "verification_uri"; + } +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/extractors/HeaderExtractorImpl.java b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/HeaderExtractorImpl.java index 2a840521c..5ffe0c023 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/extractors/HeaderExtractorImpl.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/HeaderExtractorImpl.java @@ -12,7 +12,6 @@ */ public class HeaderExtractorImpl implements HeaderExtractor { - public static final int ESTIMATED_PARAM_LENGTH = 20; private static final String PARAM_SEPARATOR = ", "; private static final String PREAMBLE = "OAuth "; @@ -23,20 +22,26 @@ public class HeaderExtractorImpl implements HeaderExtractor { public String extract(OAuthRequest request) { checkPreconditions(request); final Map parameters = request.getOauthParameters(); - final StringBuilder header = new StringBuilder(parameters.size() * ESTIMATED_PARAM_LENGTH); - header.append(PREAMBLE); - for (Map.Entry entry : parameters.entrySet()) { + + final StringBuilder header = new StringBuilder(PREAMBLE); + + for (Map.Entry parameter : parameters.entrySet()) { if (header.length() > PREAMBLE.length()) { header.append(PARAM_SEPARATOR); } - header.append(String.format("%s=\"%s\"", entry.getKey(), OAuthEncoder.encode(entry.getValue()))); + header.append(parameter.getKey()) + .append("=\"") + .append(OAuthEncoder.encode(parameter.getValue())) + .append('"'); } if (request.getRealm() != null && !request.getRealm().isEmpty()) { - header.append(PARAM_SEPARATOR); - header.append(String.format("%s=\"%s\"", OAuthConstants.REALM, request.getRealm())); + header.append(PARAM_SEPARATOR) + .append(OAuthConstants.REALM) + .append("=\"") + .append(request.getRealm()) + .append('"'); } - return header.toString(); } diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/extractors/OAuth1AccessTokenJSONExtractor.java b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/OAuth1AccessTokenJSONExtractor.java new file mode 100644 index 000000000..9267f80b4 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/OAuth1AccessTokenJSONExtractor.java @@ -0,0 +1,23 @@ +package com.github.scribejava.core.extractors; + +import com.github.scribejava.core.model.OAuth1AccessToken; + +public class OAuth1AccessTokenJSONExtractor extends AbstractOAuth1JSONTokenExtractor { + + protected OAuth1AccessTokenJSONExtractor() { + } + + @Override + protected OAuth1AccessToken createToken(String token, String secret, String response) { + return new OAuth1AccessToken(token, secret, response); + } + + private static class InstanceHolder { + + private static final OAuth1AccessTokenJSONExtractor INSTANCE = new OAuth1AccessTokenJSONExtractor(); + } + + public static OAuth1AccessTokenJSONExtractor instance() { + return InstanceHolder.INSTANCE; + } +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/extractors/OAuth1RequestTokenJSONExtractor.java b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/OAuth1RequestTokenJSONExtractor.java new file mode 100644 index 000000000..875337a82 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/OAuth1RequestTokenJSONExtractor.java @@ -0,0 +1,23 @@ +package com.github.scribejava.core.extractors; + +import com.github.scribejava.core.model.OAuth1RequestToken; + +public class OAuth1RequestTokenJSONExtractor extends AbstractOAuth1JSONTokenExtractor { + + protected OAuth1RequestTokenJSONExtractor() { + } + + @Override + protected OAuth1RequestToken createToken(String token, String secret, String response) { + return new OAuth1RequestToken(token, secret, response); + } + + private static class InstanceHolder { + + private static final OAuth1RequestTokenJSONExtractor INSTANCE = new OAuth1RequestTokenJSONExtractor(); + } + + public static OAuth1RequestTokenJSONExtractor instance() { + return InstanceHolder.INSTANCE; + } +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/extractors/OAuth2AccessTokenExtractor.java b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/OAuth2AccessTokenExtractor.java index 8af44d3aa..855a03c43 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/extractors/OAuth2AccessTokenExtractor.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/OAuth2AccessTokenExtractor.java @@ -3,6 +3,7 @@ import java.io.IOException; import java.util.regex.Matcher; import java.util.regex.Pattern; + import com.github.scribejava.core.exceptions.OAuthException; import com.github.scribejava.core.model.OAuth2AccessToken; import com.github.scribejava.core.model.Response; @@ -37,6 +38,9 @@ public static OAuth2AccessTokenExtractor instance() { */ @Override public OAuth2AccessToken extract(Response response) throws IOException { + if (response.getCode() != 200) { + throw new OAuthException("Response code is not 200 but '" + response.getCode() + '\''); + } final String body = response.getBody(); Preconditions.checkEmptyString(body, "Response body is incorrect. Can't extract a token from an empty string"); 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 0c24755b3..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,29 +1,20 @@ 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; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import com.github.scribejava.core.exceptions.OAuthException; import com.github.scribejava.core.model.OAuth2AccessToken; import com.github.scribejava.core.model.OAuth2AccessTokenErrorResponse; +import com.github.scribejava.core.model.OAuthConstants; import com.github.scribejava.core.model.Response; +import com.github.scribejava.core.oauth2.OAuth2Error; import com.github.scribejava.core.utils.Preconditions; /** * JSON (default) implementation of {@link TokenExtractor} for OAuth 2.0 */ -public class OAuth2AccessTokenJsonExtractor implements TokenExtractor { - - private static final Pattern ACCESS_TOKEN_REGEX_PATTERN = Pattern.compile("\"access_token\"\\s*:\\s*\"(\\S*?)\""); - private static final Pattern TOKEN_TYPE_REGEX_PATTERN = Pattern.compile("\"token_type\"\\s*:\\s*\"(\\S*?)\""); - private static final Pattern EXPIRES_IN_REGEX_PATTERN = Pattern.compile("\"expires_in\"\\s*:\\s*\"?(\\d*?)\"?\\D"); - private static final Pattern REFRESH_TOKEN_REGEX_PATTERN = Pattern.compile("\"refresh_token\"\\s*:\\s*\"(\\S*?)\""); - private static final Pattern SCOPE_REGEX_PATTERN = Pattern.compile("\"scope\"\\s*:\\s*\"(\\S*?)\""); - private static final Pattern ERROR_REGEX_PATTERN = Pattern.compile("\"error\"\\s*:\\s*\"(\\S*?)\""); - private static final Pattern ERROR_DESCRIPTION_REGEX_PATTERN - = Pattern.compile("\"error_description\"\\s*:\\s*\"([^\"]*?)\""); - private static final Pattern ERROR_URI_REGEX_PATTERN = Pattern.compile("\"error_uri\"\\s*:\\s*\"(\\S*?)\""); +public class OAuth2AccessTokenJsonExtractor extends AbstractJsonExtractor implements TokenExtractor { protected OAuth2AccessTokenJsonExtractor() { } @@ -40,11 +31,10 @@ public static OAuth2AccessTokenJsonExtractor instance() { @Override public OAuth2AccessToken extract(Response response) throws IOException { final String body = response.getBody(); - Preconditions.checkEmptyString(body, - "Response body is incorrect. Can't extract a token from an empty string"); + Preconditions.checkEmptyString(body, "Response body is incorrect. Can't extract a token from an empty string"); if (response.getCode() != 200) { - generateError(response.getBody()); + generateError(response); } return createToken(body); } @@ -53,55 +43,58 @@ public OAuth2AccessToken extract(Response response) throws IOException { * Related documentation: https://tools.ietf.org/html/rfc6749#section-5.2 * * @param response response + * @throws java.io.IOException IOException + * */ - protected void generateError(String response) { - final String errorInString = extractParameter(response, ERROR_REGEX_PATTERN, true); - final String errorDescription = extractParameter(response, ERROR_DESCRIPTION_REGEX_PATTERN, false); - final String errorUriInString = extractParameter(response, ERROR_URI_REGEX_PATTERN, false); + 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 = responseBodyJson.get("error_uri"); URI errorUri; try { - errorUri = errorUriInString == null ? null : URI.create(errorUriInString); + errorUri = errorUriInString == null ? null : URI.create(errorUriInString.asText()); } catch (IllegalArgumentException iae) { errorUri = null; } - throw new OAuth2AccessTokenErrorResponse(OAuth2AccessTokenErrorResponse.ErrorCode.valueOf(errorInString), - errorDescription, errorUri, response); - } - - private OAuth2AccessToken createToken(String response) { - final String accessToken = extractParameter(response, ACCESS_TOKEN_REGEX_PATTERN, true); - final String tokenType = extractParameter(response, TOKEN_TYPE_REGEX_PATTERN, false); - final String expiresInString = extractParameter(response, EXPIRES_IN_REGEX_PATTERN, false); - Integer expiresIn; + OAuth2Error errorCode; try { - expiresIn = expiresInString == null ? null : Integer.valueOf(expiresInString); - } catch (NumberFormatException nfe) { - expiresIn = null; + errorCode = OAuth2Error + .parseFrom(extractRequiredParameter(responseBodyJson, "error", responseBody).asText()); + } catch (IllegalArgumentException iaE) { + //non oauth standard error code + errorCode = null; } - final String refreshToken = extractParameter(response, REFRESH_TOKEN_REGEX_PATTERN, false); - final String scope = extractParameter(response, SCOPE_REGEX_PATTERN, false); - return createToken(accessToken, tokenType, expiresIn, refreshToken, scope, response); - } + final JsonNode errorDescription = responseBodyJson.get("error_description"); - protected OAuth2AccessToken createToken(String accessToken, String tokenType, Integer expiresIn, - String refreshToken, String scope, String response) { - return new OAuth2AccessToken(accessToken, tokenType, expiresIn, refreshToken, scope, response); + throw new OAuth2AccessTokenErrorResponse(errorCode, errorDescription == null ? null : errorDescription.asText(), + errorUri, response); } - protected static String extractParameter(String response, Pattern regexPattern, boolean required) - throws OAuthException { - final Matcher matcher = regexPattern.matcher(response); - if (matcher.find()) { - return matcher.group(1); - } + private OAuth2AccessToken createToken(String rawResponse) throws IOException { - if (required) { - throw new OAuthException("Response body is incorrect. Can't extract a '" + regexPattern.pattern() - + "' from this: '" + response + "'", null); - } + final JsonNode response = OBJECT_MAPPER.readTree(rawResponse); + + final JsonNode expiresInNode = response.get("expires_in"); + final JsonNode refreshToken = response.get(OAuthConstants.REFRESH_TOKEN); + final JsonNode scope = response.get(OAuthConstants.SCOPE); + final JsonNode tokenType = response.get("token_type"); - return null; + return createToken(extractRequiredParameter(response, OAuthConstants.ACCESS_TOKEN, rawResponse).asText(), + tokenType == null ? null : tokenType.asText(), expiresInNode == null ? null : expiresInNode.asInt(), + refreshToken == null ? null : refreshToken.asText(), scope == null ? null : scope.asText(), response, + rawResponse); + } + + protected OAuth2AccessToken createToken(String accessToken, String tokenType, Integer expiresIn, + String refreshToken, String scope, JsonNode response, String rawResponse) { + return new OAuth2AccessToken(accessToken, tokenType, expiresIn, refreshToken, scope, rawResponse); } } 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 091a95849..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; @@ -13,21 +13,90 @@ public abstract class AbstractAsyncOnlyHttpClient implements HttpClient { @Override 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 + public Response execute(String userAgent, Map headers, Verb httpVerb, String completeUrl, + com.github.scribejava.core.httpclient.multipart.MultipartPayload bodyContents) + throws InterruptedException, ExecutionException, IOException { + + 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/HttpClient.java b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/HttpClient.java index 5793faf5c..821c9b194 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/HttpClient.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/HttpClient.java @@ -1,25 +1,30 @@ package com.github.scribejava.core.httpclient; +import com.github.scribejava.core.httpclient.multipart.MultipartPayload; import com.github.scribejava.core.model.OAuthAsyncRequestCallback; import com.github.scribejava.core.model.OAuthRequest; import com.github.scribejava.core.model.Response; import com.github.scribejava.core.model.Verb; +import java.io.Closeable; import java.io.File; import java.io.IOException; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; -public interface HttpClient { +public interface HttpClient extends Closeable { + String DEFAULT_CONTENT_TYPE = "application/x-www-form-urlencoded"; String CONTENT_TYPE = "Content-Type"; String CONTENT_LENGTH = "Content-Length"; - void close() throws IOException; - Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, byte[] bodyContents, OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter); + Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, + MultipartPayload bodyContents, OAuthAsyncRequestCallback callback, + OAuthRequest.ResponseConverter converter); + Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, String bodyContents, OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter); @@ -29,6 +34,9 @@ Future executeAsync(String userAgent, Map headers, Verb h Response execute(String userAgent, Map headers, Verb httpVerb, String completeUrl, byte[] bodyContents) throws InterruptedException, ExecutionException, IOException; + Response execute(String userAgent, Map headers, Verb httpVerb, String completeUrl, + MultipartPayload bodyContents) throws InterruptedException, ExecutionException, IOException; + Response execute(String userAgent, Map headers, Verb httpVerb, String completeUrl, String bodyContents) throws InterruptedException, ExecutionException, IOException; diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/jdk/JDKHttpClient.java b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/jdk/JDKHttpClient.java index ca38aec48..b500ba997 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/jdk/JDKHttpClient.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/jdk/JDKHttpClient.java @@ -2,13 +2,17 @@ import com.github.scribejava.core.exceptions.OAuthException; import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.multipart.MultipartPayload; +import com.github.scribejava.core.httpclient.multipart.MultipartUtils; 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.Response; import com.github.scribejava.core.model.Verb; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; +import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.UnknownHostException; @@ -22,37 +26,61 @@ public class JDKHttpClient implements HttpClient { private final JDKHttpClientConfig config; + public JDKHttpClient() { + this(JDKHttpClientConfig.defaultConfig()); + } + public JDKHttpClient(JDKHttpClientConfig clientConfig) { config = clientConfig; } @Override - public void close() throws IOException { + public void close() { } @Override public Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, byte[] bodyContents, OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter) { - try { - final T response = converter.convert(execute(userAgent, headers, httpVerb, completeUrl, bodyContents)); - callback.onCompleted(response); - return new JDKHttpFuture<>(response); - } catch (InterruptedException | ExecutionException | IOException e) { - callback.onThrowable(e); - return new JDKHttpFuture<>(e); - } + + return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, BodyType.BYTE_ARRAY, bodyContents, callback, + converter); + } + + @Override + public Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, + MultipartPayload bodyContents, OAuthAsyncRequestCallback callback, + OAuthRequest.ResponseConverter converter) { + + return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, BodyType.MULTIPART, bodyContents, callback, + converter); } @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, BodyType.STRING, bodyContents, callback, + converter); + } + + @Override + public Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, + File bodyContents, OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter) { + throw new UnsupportedOperationException("JDKHttpClient does not support File payload for the moment"); + } + + private Future doExecuteAsync(String userAgent, Map headers, Verb httpVerb, + String completeUrl, BodyType bodyType, Object bodyContents, OAuthAsyncRequestCallback callback, + OAuthRequest.ResponseConverter converter) { try { - final T response = converter.convert(execute(userAgent, headers, httpVerb, completeUrl, bodyContents)); + final Response response = doExecute(userAgent, headers, httpVerb, completeUrl, bodyType, bodyContents); + @SuppressWarnings("unchecked") + final T t = converter == null ? (T) response : converter.convert(response); if (callback != null) { - callback.onCompleted(response); + callback.onCompleted(t); } - return new JDKHttpFuture<>(response); - } catch (InterruptedException | ExecutionException | IOException e) { + return new JDKHttpFuture<>(t); + } catch (IOException | RuntimeException e) { if (callback != null) { callback.onThrowable(e); } @@ -61,15 +89,15 @@ public Future executeAsync(String userAgent, Map headers, } @Override - public Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, - File bodyContents, OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter) { - throw new UnsupportedOperationException("JDKHttpClient do not support File payload for the moment"); + public Response execute(String userAgent, Map headers, Verb httpVerb, String completeUrl, + byte[] bodyContents) throws InterruptedException, ExecutionException, IOException { + return doExecute(userAgent, headers, httpVerb, completeUrl, BodyType.BYTE_ARRAY, bodyContents); } @Override public Response execute(String userAgent, Map headers, Verb httpVerb, String completeUrl, - byte[] bodyContents) throws InterruptedException, ExecutionException, IOException { - return doExecute(userAgent, headers, httpVerb, completeUrl, BodyType.BYTE_ARRAY, bodyContents); + MultipartPayload multipartPayloads) throws InterruptedException, ExecutionException, IOException { + return doExecute(userAgent, headers, httpVerb, completeUrl, BodyType.MULTIPART, multipartPayloads); } @Override @@ -81,12 +109,18 @@ public Response execute(String userAgent, Map headers, Verb http @Override public Response execute(String userAgent, Map headers, Verb httpVerb, String completeUrl, File bodyContents) throws InterruptedException, ExecutionException, IOException { - throw new UnsupportedOperationException("JDKHttpClient do not support File payload for the moment"); + throw new UnsupportedOperationException("JDKHttpClient does not support File payload for the moment"); } private Response doExecute(String userAgent, Map headers, Verb httpVerb, String completeUrl, BodyType bodyType, Object bodyContents) throws IOException { - final HttpURLConnection connection = (HttpURLConnection) new URL(completeUrl).openConnection(); + final URL url = new URL(completeUrl); + final HttpURLConnection connection; + if (config.getProxy() == null) { + connection = (HttpURLConnection) url.openConnection(); + } else { + connection = (HttpURLConnection) url.openConnection(config.getProxy()); + } connection.setInstanceFollowRedirects(config.isFollowRedirects()); connection.setRequestMethod(httpVerb.name()); if (config.getConnectTimeout() != null) { @@ -118,6 +152,12 @@ void setBody(HttpURLConnection connection, Object bodyContents, boolean requires addBody(connection, (byte[]) bodyContents, requiresBody); } }, + MULTIPART { + @Override + void setBody(HttpURLConnection connection, Object bodyContents, boolean requiresBody) throws IOException { + addBody(connection, (MultipartPayload) bodyContents, requiresBody); + } + }, STRING { @Override void setBody(HttpURLConnection connection, Object bodyContents, boolean requiresBody) throws IOException { @@ -131,21 +171,24 @@ abstract void setBody(HttpURLConnection connection, Object bodyContents, boolean private static Map parseHeaders(HttpURLConnection conn) { final Map headers = new HashMap<>(); - for (Map.Entry> entry : conn.getHeaderFields().entrySet()) { - final String key = entry.getKey(); + + for (Map.Entry> headerField : conn.getHeaderFields().entrySet()) { + final String key = headerField.getKey(); + final String value = headerField.getValue().get(0); if ("Content-Encoding".equalsIgnoreCase(key)) { - headers.put("Content-Encoding", entry.getValue().get(0)); + headers.put("Content-Encoding", value); } else { - headers.put(key, entry.getValue().get(0)); + headers.put(key, value); } } return headers; } private static void addHeaders(HttpURLConnection connection, Map headers, String userAgent) { - for (Map.Entry entry : headers.entrySet()) { - connection.setRequestProperty(entry.getKey(), entry.getValue()); + for (Map.Entry header : headers.entrySet()) { + connection.setRequestProperty(header.getKey(), header.getValue()); } + if (userAgent != null) { connection.setRequestProperty(OAuthConstants.USER_AGENT_HEADER_NAME, userAgent); } @@ -154,13 +197,38 @@ private static void addHeaders(HttpURLConnection connection, Map private static void addBody(HttpURLConnection connection, byte[] content, boolean requiresBody) throws IOException { final int contentLength = content.length; if (requiresBody || contentLength > 0) { - connection.setRequestProperty(CONTENT_LENGTH, String.valueOf(contentLength)); + final OutputStream outputStream = prepareConnectionForBodyAndGetOutputStream(connection, contentLength); + if (contentLength > 0) { + outputStream.write(content); + } + } + } + + private static void addBody(HttpURLConnection connection, MultipartPayload multipartPayload, boolean requiresBody) + throws IOException { + + for (Map.Entry header : multipartPayload.getHeaders().entrySet()) { + connection.setRequestProperty(header.getKey(), header.getValue()); + } - if (connection.getRequestProperty(CONTENT_TYPE) == null) { - connection.setRequestProperty(CONTENT_TYPE, DEFAULT_CONTENT_TYPE); + if (requiresBody) { + final ByteArrayOutputStream os = MultipartUtils.getPayload(multipartPayload); + final int contentLength = os.size(); + final OutputStream outputStream = prepareConnectionForBodyAndGetOutputStream(connection, contentLength); + if (contentLength > 0) { + os.writeTo(outputStream); } - connection.setDoOutput(true); - connection.getOutputStream().write(content); } } + + private static OutputStream prepareConnectionForBodyAndGetOutputStream(HttpURLConnection connection, + int contentLength) throws IOException { + + connection.setRequestProperty(CONTENT_LENGTH, String.valueOf(contentLength)); + if (connection.getRequestProperty(CONTENT_TYPE) == null) { + connection.setRequestProperty(CONTENT_TYPE, DEFAULT_CONTENT_TYPE); + } + connection.setDoOutput(true); + return connection.getOutputStream(); + } } diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/jdk/JDKHttpClientConfig.java b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/jdk/JDKHttpClientConfig.java index cfb97b359..3b322b0af 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/jdk/JDKHttpClientConfig.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/jdk/JDKHttpClientConfig.java @@ -1,5 +1,7 @@ package com.github.scribejava.core.httpclient.jdk; +import java.net.Proxy; + import com.github.scribejava.core.httpclient.HttpClientConfig; public class JDKHttpClientConfig implements HttpClientConfig { @@ -7,6 +9,7 @@ public class JDKHttpClientConfig implements HttpClientConfig { private Integer connectTimeout; private Integer readTimeout; private boolean followRedirects = true; + private Proxy proxy; @Override public JDKHttpClientConfig createDefaultConfig() { @@ -25,6 +28,11 @@ public void setConnectTimeout(Integer connectTimeout) { this.connectTimeout = connectTimeout; } + public JDKHttpClientConfig withConnectTimeout(Integer connectTimeout) { + this.connectTimeout = connectTimeout; + return this; + } + public Integer getReadTimeout() { return readTimeout; } @@ -33,6 +41,24 @@ public void setReadTimeout(Integer readTimeout) { this.readTimeout = readTimeout; } + public JDKHttpClientConfig withReadTimeout(Integer readTimeout) { + this.readTimeout = readTimeout; + return this; + } + + public void setProxy(Proxy proxy) { + this.proxy = proxy; + } + + public Proxy getProxy() { + return proxy; + } + + public JDKHttpClientConfig withProxy(Proxy proxy) { + this.proxy = proxy; + return this; + } + public boolean isFollowRedirects() { return followRedirects; } @@ -49,4 +75,19 @@ public boolean isFollowRedirects() { public void setFollowRedirects(boolean followRedirects) { this.followRedirects = followRedirects; } + + /** + * Sets whether the underlying Http Connection follows redirects or not. + * + * Defaults to true (follow redirects) + * + * @see http://docs.oracle.com/javase/6/docs/api/java/net/HttpURLConnection.html#setInstanceFollowRedirects(boolean) + * @param followRedirects boolean + * @return this for chaining methods invocations + */ + public JDKHttpClientConfig withFollowRedirects(boolean followRedirects) { + this.followRedirects = followRedirects; + return this; + } } 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/BodyPartPayload.java b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/multipart/BodyPartPayload.java new file mode 100644 index 000000000..ddd6e9cb2 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/multipart/BodyPartPayload.java @@ -0,0 +1,30 @@ +package com.github.scribejava.core.httpclient.multipart; + +import com.github.scribejava.core.httpclient.HttpClient; +import java.util.Collections; +import java.util.Map; + +public abstract class BodyPartPayload { + + private final Map headers; + + public BodyPartPayload() { + this((Map) null); + } + + public BodyPartPayload(String contentType) { + this(convertContentTypeToHeaders(contentType)); + } + + public BodyPartPayload(Map headers) { + this.headers = headers; + } + + public Map getHeaders() { + return headers; + } + + protected static Map convertContentTypeToHeaders(String contentType) { + return contentType == null ? null : Collections.singletonMap(HttpClient.CONTENT_TYPE, contentType); + } +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/multipart/ByteArrayBodyPartPayload.java b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/multipart/ByteArrayBodyPartPayload.java new file mode 100644 index 000000000..c706d7481 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/multipart/ByteArrayBodyPartPayload.java @@ -0,0 +1,50 @@ +package com.github.scribejava.core.httpclient.multipart; + +import java.util.Map; + +public class ByteArrayBodyPartPayload extends BodyPartPayload { + + private final byte[] payload; + private final int off; + private final int len; + + public ByteArrayBodyPartPayload(byte[] payload, int off, int len, Map headers) { + super(headers); + this.payload = payload; + this.off = off; + this.len = len; + } + + public ByteArrayBodyPartPayload(byte[] payload, Map headers) { + this(payload, 0, payload.length, headers); + } + + public ByteArrayBodyPartPayload(byte[] payload, String contentType) { + this(payload, convertContentTypeToHeaders(contentType)); + } + + public ByteArrayBodyPartPayload(byte[] payload, int off, int len, String contentType) { + this(payload, off, len, convertContentTypeToHeaders(contentType)); + } + + public ByteArrayBodyPartPayload(byte[] payload) { + this(payload, (Map) null); + } + + public ByteArrayBodyPartPayload(byte[] payload, int off, int len) { + this(payload, off, len, (Map) null); + } + + public byte[] getPayload() { + return payload; + } + + public int getOff() { + return off; + } + + public int getLen() { + return len; + } + +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/multipart/FileByteArrayBodyPartPayload.java b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/multipart/FileByteArrayBodyPartPayload.java new file mode 100644 index 000000000..41b3e72bf --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/multipart/FileByteArrayBodyPartPayload.java @@ -0,0 +1,77 @@ +package com.github.scribejava.core.httpclient.multipart; + +import com.github.scribejava.core.httpclient.HttpClient; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class FileByteArrayBodyPartPayload extends ByteArrayBodyPartPayload { + + public FileByteArrayBodyPartPayload(byte[] payload) { + this(payload, null); + } + + public FileByteArrayBodyPartPayload(byte[] payload, int off, int len) { + this(payload, off, len, null); + } + + public FileByteArrayBodyPartPayload(byte[] payload, String name) { + this(payload, name, null); + } + + public FileByteArrayBodyPartPayload(byte[] payload, int off, int len, String name) { + this(payload, off, len, name, null); + } + + public FileByteArrayBodyPartPayload(byte[] payload, String name, String filename) { + this(null, payload, name, filename); + } + + public FileByteArrayBodyPartPayload(byte[] payload, int off, int len, String name, String filename) { + this(null, payload, off, len, name, filename); + } + + public FileByteArrayBodyPartPayload(String contentType, byte[] payload) { + this(contentType, payload, null); + } + + public FileByteArrayBodyPartPayload(String contentType, byte[] payload, int off, int len) { + this(contentType, payload, off, len, null); + } + + public FileByteArrayBodyPartPayload(String contentType, byte[] payload, String name) { + this(contentType, payload, name, null); + } + + public FileByteArrayBodyPartPayload(String contentType, byte[] payload, int off, int len, String name) { + this(contentType, payload, off, len, name, null); + } + + public FileByteArrayBodyPartPayload(String contentType, byte[] payload, String name, String filename) { + super(payload, composeHeaders(contentType, name, filename)); + } + + public FileByteArrayBodyPartPayload(String contentType, byte[] payload, int off, int len, String name, + String filename) { + super(payload, off, len, composeHeaders(contentType, name, filename)); + } + + private static Map composeHeaders(String contentType, String name, String filename) { + + String contentDispositionHeader = "form-data"; + if (name != null) { + contentDispositionHeader += "; name=\"" + name + '"'; + } + if (filename != null) { + contentDispositionHeader += "; filename=\"" + filename + '"'; + } + if (contentType == null) { + return Collections.singletonMap("Content-Disposition", contentDispositionHeader); + } else { + final Map headers = new HashMap<>(); + headers.put(HttpClient.CONTENT_TYPE, contentType); + headers.put("Content-Disposition", contentDispositionHeader); + return headers; + } + } +} 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 new file mode 100644 index 000000000..9d60e0caa --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/multipart/MultipartPayload.java @@ -0,0 +1,110 @@ +package com.github.scribejava.core.httpclient.multipart; + +import com.github.scribejava.core.httpclient.HttpClient; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class MultipartPayload extends BodyPartPayload { + + private static final String DEFAULT_SUBTYPE = "form-data"; + + private final String boundary; + private String preamble; + private final List bodyParts = new ArrayList<>(); + private String epilogue; + + public MultipartPayload() { + this(null, MultipartUtils.generateDefaultBoundary(), null); + } + + public MultipartPayload(String boundary) { + this(null, boundary, null); + } + + public MultipartPayload(String subtype, String boundary) { + this(subtype, boundary, null); + } + + public MultipartPayload(Map headers) { + this(null, parseOrGenerateBoundary(headers), headers); + } + + public MultipartPayload(String boundary, Map headers) { + this(null, boundary, headers); + } + + public MultipartPayload(String subtype, String boundary, Map headers) { + super(composeHeaders(subtype, boundary, headers)); + this.boundary = boundary; + } + + private static Map composeHeaders(String subtype, String boundary, Map headersIn) + throws IllegalArgumentException { + MultipartUtils.checkBoundarySyntax(boundary); + final Map headersOut; + String contentTypeHeader = headersIn == null ? null : headersIn.get(HttpClient.CONTENT_TYPE); + if (contentTypeHeader == null) { + contentTypeHeader = "multipart/" + (subtype == null ? DEFAULT_SUBTYPE : subtype) + + "; boundary=\"" + boundary + '"'; + if (headersIn == null) { + headersOut = Collections.singletonMap(HttpClient.CONTENT_TYPE, contentTypeHeader); + } else { + headersOut = headersIn; + headersOut.put(HttpClient.CONTENT_TYPE, contentTypeHeader); + } + } else { + headersOut = headersIn; + final String parsedBoundary = MultipartUtils.parseBoundaryFromHeader(contentTypeHeader); + if (parsedBoundary == null) { + headersOut.put(HttpClient.CONTENT_TYPE, contentTypeHeader + "; boundary=\"" + boundary + '"'); + } else if (!parsedBoundary.equals(boundary)) { + throw new IllegalArgumentException( + "Different boundaries was passed in constructors. One as argument, second as header"); + } + } + return headersOut; + } + + private static String parseOrGenerateBoundary(Map headers) { + final String parsedBoundary = MultipartUtils.parseBoundaryFromHeader(headers.get(HttpClient.CONTENT_TYPE)); + return parsedBoundary == null ? MultipartUtils.generateDefaultBoundary() : parsedBoundary; + } + + public void addBodyPart(BodyPartPayload bodyPartPayload) { + bodyParts.add(bodyPartPayload); + } + + public void addBodyPart(MultipartPayload multipartPayload) { + if (multipartPayload.getBoundary().equals(boundary)) { + throw new IllegalArgumentException("{'boundary'}={'" + boundary + + "'} is the same for parent MultipartPayload and child"); + } + bodyParts.add(multipartPayload); + } + + public List getBodyParts() { + return bodyParts; + } + + public String getBoundary() { + return boundary; + } + + public String getPreamble() { + return preamble; + } + + public void setPreamble(String preamble) { + this.preamble = preamble; + } + + public String getEpilogue() { + return epilogue; + } + + public void setEpilogue(String epilogue) { + this.epilogue = epilogue; + } +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/multipart/MultipartUtils.java b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/multipart/MultipartUtils.java new file mode 100644 index 000000000..bdbeaf542 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/multipart/MultipartUtils.java @@ -0,0 +1,84 @@ +package com.github.scribejava.core.httpclient.multipart; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class MultipartUtils { + + private static final String B_CHARS_NO_SPACE_PATTERN = "0-9a-zA-Z'()+_,-./:=?"; + private static final String B_CHARS_PATTERN = B_CHARS_NO_SPACE_PATTERN + " "; + private static final String BOUNDARY_PATTERN = '[' + B_CHARS_PATTERN + "]{0,69}[" + B_CHARS_NO_SPACE_PATTERN + ']'; + private static final Pattern BOUNDARY_REGEXP = Pattern.compile(BOUNDARY_PATTERN); + private static final Pattern BOUNDARY_FROM_HEADER_REGEXP + = Pattern.compile("; boundary=\"?(" + BOUNDARY_PATTERN + ")\"?"); + + private MultipartUtils() { + } + + public static void checkBoundarySyntax(String boundary) { + if (boundary == null || !BOUNDARY_REGEXP.matcher(boundary).matches()) { + throw new IllegalArgumentException("{'boundary'='" + boundary + "'} has invalid syntax. Should be '" + + BOUNDARY_PATTERN + "'."); + } + } + + public static String parseBoundaryFromHeader(String contentTypeHeader) { + if (contentTypeHeader == null) { + return null; + } + final Matcher matcher = BOUNDARY_FROM_HEADER_REGEXP.matcher(contentTypeHeader); + return matcher.find() ? matcher.group(1) : null; + } + + public static String generateDefaultBoundary() { + return "----ScribeJava----" + System.currentTimeMillis(); + } + + public static ByteArrayOutputStream getPayload(MultipartPayload multipartPayload) throws IOException { + final ByteArrayOutputStream os = new ByteArrayOutputStream(); + + final String preamble = multipartPayload.getPreamble(); + if (preamble != null) { + os.write((preamble + "\r\n").getBytes()); + } + final List bodyParts = multipartPayload.getBodyParts(); + if (!bodyParts.isEmpty()) { + final String boundary = multipartPayload.getBoundary(); + final byte[] startBoundary = ("--" + boundary + "\r\n").getBytes(); + + for (BodyPartPayload bodyPart : bodyParts) { + os.write(startBoundary); + + final Map bodyPartHeaders = bodyPart.getHeaders(); + if (bodyPartHeaders != null) { + for (Map.Entry header : bodyPartHeaders.entrySet()) { + os.write((header.getKey() + ": " + header.getValue() + "\r\n").getBytes()); + } + } + + os.write("\r\n".getBytes()); + if (bodyPart instanceof MultipartPayload) { + getPayload((MultipartPayload) bodyPart).writeTo(os); + } else if (bodyPart instanceof ByteArrayBodyPartPayload) { + final ByteArrayBodyPartPayload byteArrayBodyPart = (ByteArrayBodyPartPayload) bodyPart; + os.write(byteArrayBodyPart.getPayload(), byteArrayBodyPart.getOff(), byteArrayBodyPart.getLen()); + } else { + throw new AssertionError(bodyPart.getClass()); + } + os.write("\r\n".getBytes()); //CRLF for the next (starting or closing) boundary + } + + os.write(("--" + boundary + "--").getBytes()); + final String epilogue = multipartPayload.getEpilogue(); + if (epilogue != null) { + os.write(("\r\n" + epilogue).getBytes()); + } + + } + return os; + } +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/model/DeviceAuthorization.java b/scribejava-core/src/main/java/com/github/scribejava/core/model/DeviceAuthorization.java new file mode 100644 index 000000000..dfdc42c36 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/model/DeviceAuthorization.java @@ -0,0 +1,105 @@ +package com.github.scribejava.core.model; + +/** + * Device Authorization Response + * + * @see rfc8628 + */ +public class DeviceAuthorization { + + /** + * device_code + * + * REQUIRED. The device verification code. + */ + private final String deviceCode; + + /** + * user_code + * + * REQUIRED. The end-user verification code. + */ + private final String userCode; + + /** + * verification_uri + * + * REQUIRED. The end-user verification URI on the authorization server. The URI should be short and easy to remember + * as end users will be asked to manually type it into their user agent. + */ + private final String verificationUri; + + /** + * verification_uri_complete + * + * OPTIONAL. A verification URI that includes the "user_code" (or other information with the same function as the + * "user_code"), which is designed for non-textual transmission. + */ + private String verificationUriComplete; + + /** + * expires_in + * + * REQUIRED. The lifetime in seconds of the "device_code" and "user_code". + */ + private final int expiresInSeconds; + + /** + * interval + * + * OPTIONAL. The minimum amount of time in seconds that the client SHOULD wait between polling requests to the token + * endpoint. If no value is provided, clients MUST use 5 as the default. + */ + private int intervalSeconds = 5; + + public DeviceAuthorization(String deviceCode, String userCode, String verificationUri, int expiresInSeconds) { + this.deviceCode = deviceCode; + this.userCode = userCode; + this.verificationUri = verificationUri; + this.expiresInSeconds = expiresInSeconds; + } + + public void setVerificationUriComplete(String verificationUriComplete) { + this.verificationUriComplete = verificationUriComplete; + } + + public void setIntervalSeconds(int intervalSeconds) { + this.intervalSeconds = intervalSeconds; + } + + public String getDeviceCode() { + return deviceCode; + } + + public String getUserCode() { + return userCode; + } + + public String getVerificationUri() { + return verificationUri; + } + + public String getVerificationUriComplete() { + return verificationUriComplete; + } + + public long getExpiresInSeconds() { + return expiresInSeconds; + } + + public int getIntervalSeconds() { + return intervalSeconds; + } + + @Override + public String toString() { + return "DeviceAuthorization{" + + "'deviceCode'='" + deviceCode + + "', 'userCode'='" + userCode + + "', 'verificationUri'='" + verificationUri + + "', 'verificationUriComplete'='" + verificationUriComplete + + "', 'expiresInSeconds'='" + expiresInSeconds + + "', 'intervalSeconds'='" + intervalSeconds + "'}"; + } + +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuth1AccessToken.java b/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuth1AccessToken.java index e57a7db39..dd145e5c7 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuth1AccessToken.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuth1AccessToken.java @@ -62,11 +62,4 @@ public boolean equals(Object obj) { } return Objects.equals(getTokenSecret(), other.getTokenSecret()); } - - @Override - public String toString() { - return "OAuth1AccessToken{" - + "oauth_token=" + getToken() - + ", oauth_token_secret=" + getTokenSecret() + '}'; - } } diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuth1RequestToken.java b/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuth1RequestToken.java index 1c53d4932..b8b3d2be4 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuth1RequestToken.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuth1RequestToken.java @@ -83,12 +83,4 @@ public boolean equals(Object obj) { } return Objects.equals(getTokenSecret(), other.getTokenSecret()); } - - @Override - public String toString() { - return "OAuth1RequestToken{" - + "oauth_token=" + getToken() - + ", oauth_token_secret=" + getTokenSecret() - + ", oauth_callback_confirmed=" + oauthCallbackConfirmed + '}'; - } } 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 b97e4e0a0..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,10 +5,9 @@ /** * Represents an OAuth 2 Access token. - *

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

+ * @see OAuth 2 Access Token Specification */ public class OAuth2AccessToken extends Token { @@ -73,7 +72,6 @@ public OAuth2AccessToken(String accessToken, String tokenType, Integer expiresIn this.scope = scope; } - public String getAccessToken() { return accessToken; } @@ -131,14 +129,4 @@ public boolean equals(Object obj) { } return Objects.equals(expiresIn, other.getExpiresIn()); } - - @Override - public String toString() { - return "OAuth2AccessToken{" - + "access_token=" + accessToken - + ", token_type=" + tokenType - + ", expires_in=" + expiresIn - + ", refresh_token=" + refreshToken - + ", scope=" + scope + '}'; - } } 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 eba31404e..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,39 +1,31 @@ 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; - public enum ErrorCode { - invalid_request, invalid_client, invalid_grant, unauthorized_client, unsupported_grant_type, invalid_scope - } - - private final ErrorCode errorCode; + private final OAuth2Error error; private final String errorDescription; private final URI errorUri; - private final String rawResponse; - public OAuth2AccessTokenErrorResponse(ErrorCode errorCode, String errorDescription, URI errorUri, - String rawResponse) { + public OAuth2AccessTokenErrorResponse(OAuth2Error error, String errorDescription, URI errorUri, + Response rawResponse) throws IOException { super(rawResponse); - if (errorCode == null) { - throw new IllegalArgumentException("errorCode must not be null"); - } - this.errorCode = errorCode; + this.error = error; this.errorDescription = errorDescription; this.errorUri = errorUri; - this.rawResponse = rawResponse; } - public ErrorCode getErrorCode() { - return errorCode; + public OAuth2Error getError() { + return error; } public String getErrorDescription() { @@ -44,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/OAuthAsyncRequestCallback.java b/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuthAsyncRequestCallback.java index 10f398fc4..7719314d2 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuthAsyncRequestCallback.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuthAsyncRequestCallback.java @@ -2,6 +2,11 @@ public interface OAuthAsyncRequestCallback { + /** + * Implementations of this method should close provided response in case it implements {@link java.io.Closeable} + * + * @param response response + */ void onCompleted(T response); void onThrowable(Throwable t); diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuthConfig.java b/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuthConfig.java deleted file mode 100644 index 244ca6731..000000000 --- a/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuthConfig.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.github.scribejava.core.model; - -import com.github.scribejava.core.httpclient.HttpClient; -import com.github.scribejava.core.httpclient.HttpClientConfig; -import java.io.IOException; -import java.io.OutputStream; - -/** - * Parameter object that groups OAuth config values - */ -public class OAuthConfig { - - private final String apiKey; - private final String apiSecret; - private final String callback; - private final String scope; - private final OutputStream debugStream; - private final String state; - private final String responseType; - private final String userAgent; - - private HttpClientConfig httpClientConfig; - private HttpClient httpClient; - - public OAuthConfig(String key, String secret) { - this(key, secret, null, null, null, null, null, null, null, null); - } - - public OAuthConfig(String apiKey, String apiSecret, String callback, String scope, OutputStream debugStream, - String state, String responseType, String userAgent, HttpClientConfig httpClientConfig, - HttpClient httpClient) { - this.apiKey = apiKey; - this.apiSecret = apiSecret; - this.callback = callback; - this.scope = scope; - this.debugStream = debugStream; - this.state = state; - this.responseType = responseType; - this.userAgent = userAgent; - this.httpClientConfig = httpClientConfig; - this.httpClient = httpClient; - } - - public String getApiKey() { - return apiKey; - } - - public String getApiSecret() { - return apiSecret; - } - - public String getCallback() { - return callback; - } - - public String getScope() { - return scope; - } - - public String getState() { - return state; - } - - public String getResponseType() { - return responseType; - } - - public String getUserAgent() { - return userAgent; - } - - public void log(String message) { - if (debugStream != null) { - message += '\n'; - try { - debugStream.write(message.getBytes("UTF8")); - } catch (IOException | RuntimeException e) { - throw new RuntimeException("there were problems while writting to the debug stream", e); - } - } - } - - public HttpClientConfig getHttpClientConfig() { - return httpClientConfig; - } - - public HttpClient getHttpClient() { - return httpClient; - } -} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuthConstants.java b/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuthConstants.java index 2cf8d236a..268e5f435 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuthConstants.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuthConstants.java @@ -17,12 +17,18 @@ public interface OAuthConstants { String PARAM_PREFIX = "oauth_"; String TOKEN = "oauth_token"; String TOKEN_SECRET = "oauth_token_secret"; - String OUT_OF_BAND = "oob"; String VERIFIER = "oauth_verifier"; String HEADER = "Authorization"; String SCOPE = "scope"; String BASIC = "Basic"; + // OAuth 1.0 + /** + * to indicate an out-of-band configuration + * @see The OAuth 1.0 Protocol + */ + String OOB = "oob"; + // OAuth 2.0 String ACCESS_TOKEN = "access_token"; String CLIENT_ID = "client_id"; @@ -32,6 +38,7 @@ public interface OAuthConstants { String REFRESH_TOKEN = "refresh_token"; String GRANT_TYPE = "grant_type"; String AUTHORIZATION_CODE = "authorization_code"; + String CLIENT_CREDENTIALS = "client_credentials"; String STATE = "state"; String USERNAME = "username"; String PASSWORD = "password"; @@ -40,5 +47,4 @@ public interface OAuthConstants { //not OAuth specific String USER_AGENT_HEADER_NAME = "User-Agent"; - } 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 3f50440eb..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 @@ -1,6 +1,8 @@ package com.github.scribejava.core.model; import com.github.scribejava.core.exceptions.OAuthException; +import com.github.scribejava.core.httpclient.multipart.BodyPartPayload; +import com.github.scribejava.core.httpclient.multipart.MultipartPayload; import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; @@ -9,6 +11,7 @@ import java.nio.charset.Charset; import java.util.HashMap; import java.util.Map; +import java.util.TreeMap; /** * The representation of an OAuth HttpRequest. @@ -21,13 +24,14 @@ public class OAuthRequest { private final Verb verb; private final ParameterList querystringParams = new ParameterList(); private final ParameterList bodyParams = new ParameterList(); - private final Map headers = new HashMap<>(); + private final Map headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); private String charset; private String stringPayload; private byte[] byteArrayPayload; private File filePayload; + private MultipartPayload multipartPayload; private final Map oauthParameters = new HashMap<>(); @@ -93,7 +97,7 @@ public String getCompleteUrl() { * @param value the header value */ public void addHeader(String key, String value) { - this.headers.put(key, value); + headers.put(key, value); } /** @@ -103,7 +107,7 @@ public void addHeader(String key, String value) { * @param value the parameter value */ public void addBodyParameter(String key, String value) { - this.bodyParams.add(key, value); + bodyParams.add(key, value); } /** @@ -113,7 +117,7 @@ public void addBodyParameter(String key, String value) { * @param value the parameter value */ public void addQuerystringParameter(String key, String value) { - this.querystringParams.add(key, value); + querystringParams.add(key, value); } public void addParameter(String key, String value) { @@ -124,6 +128,47 @@ public void addParameter(String key, String value) { } } + public MultipartPayload getMultipartPayload() { + return multipartPayload; + } + + public void setMultipartPayload(MultipartPayload multipartPayload) { + this.multipartPayload = multipartPayload; + } + + public void initMultipartPayload() { + this.multipartPayload = new MultipartPayload(); + } + + public void initMultipartPayload(String boundary) { + this.multipartPayload = new MultipartPayload(boundary); + } + + public void initMultipartPayload(String subtype, String boundary) { + this.multipartPayload = new MultipartPayload(subtype, boundary); + } + + public void initMultipartPayload(Map headers) { + this.multipartPayload = new MultipartPayload(headers); + } + + public void initMultipartPayload(String boundary, Map headers) { + this.multipartPayload = new MultipartPayload(boundary, headers); + } + + public void initMultipartPayload(String subtype, String boundary, Map headers) { + this.multipartPayload = new MultipartPayload(subtype, boundary, headers); + } + + public void setBodyPartPayloadInMultipartPayload(BodyPartPayload bodyPartPayload) { + initMultipartPayload(); + addBodyPartPayloadInMultipartPayload(bodyPartPayload); + } + + public void addBodyPartPayloadInMultipartPayload(BodyPartPayload bodyPartPayload) { + multipartPayload.addBodyPart(bodyPartPayload); + } + /** * Set body payload. This method is used when the HTTP body is not a form-url-encoded string, but another thing. * Like for example XML. Note: The contents are not part of the OAuth signature @@ -159,6 +204,7 @@ private void resetPayload() { stringPayload = null; byteArrayPayload = null; filePayload = null; + multipartPayload = null; } /** @@ -269,6 +315,15 @@ public void setCharset(String charsetName) { public interface ResponseConverter { + /** + * Implementations of this method should close provided Response in case response is not included in the return + * Object of type <T> Then responsibility to close response is in on the + * {@link com.github.scribejava.core.model.OAuthAsyncRequestCallback#onCompleted(java.lang.Object) } + * + * @param response response + * @return T + * @throws IOException IOException + */ T convert(Response response) throws IOException; } 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/model/Response.java b/scribejava-core/src/main/java/com/github/scribejava/core/model/Response.java index 8a4807677..553c6cda5 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/model/Response.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/model/Response.java @@ -4,14 +4,24 @@ import java.io.InputStream; import java.util.Map; import com.github.scribejava.core.utils.StreamUtils; +import java.io.Closeable; -public class Response { +/** + * An HTTP response. + * + *

+ * This response may contain a non-null body stream of the HttpUrlConnection. If so, this body must be closed to avoid + * leaking resources. Use either {@link #getBody()} or {@link #close()} to close the body. + */ +public class Response implements Closeable { private final int code; private final String message; private final Map headers; private String body; private InputStream stream; + private Closeable[] closeables; + private boolean closed; private Response(int code, String message, Map headers) { this.code = code; @@ -19,9 +29,11 @@ private Response(int code, String message, Map headers) { this.headers = headers; } - public Response(int code, String message, Map headers, InputStream stream) { + public Response(int code, String message, Map headers, InputStream stream, + Closeable... closeables) { this(code, message, headers); this.stream = stream; + this.closeables = closeables; } public Response(int code, String message, Map headers, String body) { @@ -41,10 +53,16 @@ private String parseBodyContents() throws IOException { return body; } - public final boolean isSuccessful() { + public boolean isSuccessful() { return code >= 200 && code < 400; } + /** + * Returns the response body as a string, closing the stream that backs it. Idempotent. + * + * @return body as string + * @throws IOException IO Exception + */ public String getBody() throws IOException { return body == null ? parseBodyContents() : body; } @@ -64,7 +82,7 @@ public InputStream getStream() { * * @return the status code */ - public final int getCode() { + public int getCode() { return code; } @@ -100,11 +118,37 @@ public String getHeader(String name) { @Override public String toString() { - return "Response{" + - "code=" + code + - ", message='" + message + '\'' + - ", body='" + body + '\'' + - ", headers=" + headers + - '}'; + return "Response{" + + "code=" + code + + ", message='" + message + '\'' + + ", body='" + body + '\'' + + ", headers=" + headers + + '}'; + } + + @Override + public void close() throws IOException { + if (closed) { + return; + } + IOException ioException = null; + if (closeables != null) { + for (Closeable closeable : closeables) { + if (closeable == null) { + continue; + } + try { + closeable.close(); + } catch (IOException ioE) { + if (ioException != null) { + ioException = ioE; + } + } + } + } + if (ioException != null) { + throw ioException; + } + closed = true; } } 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 new file mode 100644 index 000000000..d45377a56 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/oauth/AccessTokenRequestParams.java @@ -0,0 +1,79 @@ +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; + } + + public static AccessTokenRequestParams create(String code) { + return new AccessTokenRequestParams(code); + } + + public AccessTokenRequestParams pkceCodeVerifier(String pkceCodeVerifier) { + this.pkceCodeVerifier = pkceCodeVerifier; + return this; + } + + public AccessTokenRequestParams scope(String scope) { + this.scope = 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; + } + + public String getPkceCodeVerifier() { + return pkceCodeVerifier; + } + + public String getScope() { + return scope; + } +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/oauth/AuthorizationUrlBuilder.java b/scribejava-core/src/main/java/com/github/scribejava/core/oauth/AuthorizationUrlBuilder.java new file mode 100644 index 000000000..fd02f60dd --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/oauth/AuthorizationUrlBuilder.java @@ -0,0 +1,61 @@ +package com.github.scribejava.core.oauth; + +import com.github.scribejava.core.pkce.PKCE; +import com.github.scribejava.core.pkce.PKCEService; +import java.util.HashMap; +import java.util.Map; + +public class AuthorizationUrlBuilder { + + private final OAuth20Service oauth20Service; + + private String state; + private Map additionalParams; + private PKCE pkce; + private String scope; + + public AuthorizationUrlBuilder(OAuth20Service oauth20Service) { + this.oauth20Service = oauth20Service; + } + + public AuthorizationUrlBuilder state(String state) { + this.state = state; + return this; + } + + public AuthorizationUrlBuilder additionalParams(Map additionalParams) { + this.additionalParams = additionalParams; + return this; + } + + public AuthorizationUrlBuilder pkce(PKCE pkce) { + this.pkce = pkce; + return this; + } + + public AuthorizationUrlBuilder initPKCE() { + this.pkce = PKCEService.defaultInstance().generatePKCE(); + return this; + } + + public AuthorizationUrlBuilder scope(String scope) { + this.scope = scope; + return this; + } + + public PKCE getPkce() { + return pkce; + } + + public String build() { + final Map params; + if (pkce == null) { + params = additionalParams; + } else { + params = additionalParams == null ? new HashMap() : new HashMap<>(additionalParams); + params.putAll(pkce.getAuthorizationUrlParams()); + } + return oauth20Service.getApi().getAuthorizationUrl(oauth20Service.getResponseType(), oauth20Service.getApiKey(), + oauth20Service.getCallback(), scope == null ? oauth20Service.getDefaultScope() : scope, state, params); + } +} 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 ad9e6eda0..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 @@ -1,151 +1,167 @@ package com.github.scribejava.core.oauth; import java.io.IOException; -import java.util.Map; import java.util.concurrent.Future; import com.github.scribejava.core.builder.api.DefaultApi10a; import com.github.scribejava.core.builder.api.OAuth1SignatureType; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; import com.github.scribejava.core.model.OAuth1AccessToken; import com.github.scribejava.core.model.OAuth1RequestToken; import com.github.scribejava.core.model.OAuthAsyncRequestCallback; -import com.github.scribejava.core.model.OAuthConfig; import com.github.scribejava.core.model.OAuthConstants; import com.github.scribejava.core.model.OAuthRequest; import com.github.scribejava.core.model.Response; -import com.github.scribejava.core.services.Base64Encoder; -import com.github.scribejava.core.utils.MapUtils; +import java.io.OutputStream; +import java.util.Map; import java.util.concurrent.ExecutionException; /** * OAuth 1.0a implementation of {@link OAuthService} */ -public class OAuth10aService extends OAuthService { +public class OAuth10aService extends OAuthService { private static final String VERSION = "1.0"; private final DefaultApi10a api; + private final String scope; - /** - * Default constructor - * - * @param api OAuth1.0a api information - * @param config OAuth 1.0a configuration param object - */ - public OAuth10aService(DefaultApi10a api, OAuthConfig config) { - super(config); + public OAuth10aService(DefaultApi10a api, String apiKey, String apiSecret, String callback, String scope, + OutputStream debugStream, String userAgent, HttpClientConfig httpClientConfig, HttpClient httpClient) { + super(apiKey, apiSecret, callback, debugStream, userAgent, httpClientConfig, httpClient); this.api = api; + this.scope = scope; } - public final OAuth1RequestToken getRequestToken() throws IOException, InterruptedException, ExecutionException { - final OAuthConfig config = getConfig(); - config.log("obtaining request token from " + api.getRequestTokenEndpoint()); + public OAuth1RequestToken getRequestToken() throws IOException, InterruptedException, ExecutionException { + if (isDebug()) { + log("obtaining request token from %s", api.getRequestTokenEndpoint()); + } final OAuthRequest request = prepareRequestTokenRequest(); - config.log("sending request..."); - final Response response = execute(request); - final String body = response.getBody(); - - config.log("response status code: " + response.getCode()); - config.log("response body: " + body); - return api.getRequestTokenExtractor().extract(response); + log("sending request..."); + try ( Response response = execute(request)) { + if (isDebug()) { + final String body = response.getBody(); + log("response status code: %s", response.getCode()); + log("response body: %s", body); + } + return api.getRequestTokenExtractor().extract(response); + } } - public final Future getRequestTokenAsync() { + public Future getRequestTokenAsync() { return getRequestTokenAsync(null); } - public final Future getRequestTokenAsync( - OAuthAsyncRequestCallback callback) { - final OAuthConfig config = getConfig(); - config.log("async obtaining request token from " + api.getRequestTokenEndpoint()); + public Future getRequestTokenAsync(OAuthAsyncRequestCallback callback) { + if (isDebug()) { + log("async obtaining request token from %s", api.getRequestTokenEndpoint()); + } final OAuthRequest request = prepareRequestTokenRequest(); return execute(request, callback, new OAuthRequest.ResponseConverter() { @Override public OAuth1RequestToken convert(Response response) throws IOException { - return getApi().getRequestTokenExtractor().extract(response); + final OAuth1RequestToken token = getApi().getRequestTokenExtractor().extract(response); + response.close(); + return token; } }); } protected OAuthRequest prepareRequestTokenRequest() { final OAuthRequest request = new OAuthRequest(api.getRequestTokenVerb(), api.getRequestTokenEndpoint()); - final OAuthConfig config = getConfig(); - config.log("setting oauth_callback to " + config.getCallback()); - request.addOAuthParameter(OAuthConstants.CALLBACK, config.getCallback()); + String callback = getCallback(); + if (callback == null) { + callback = OAuthConstants.OOB; + } + if (isDebug()) { + log("setting oauth_callback to %s", callback); + } + request.addOAuthParameter(OAuthConstants.CALLBACK, callback); addOAuthParams(request, ""); appendSignature(request); return request; } protected void addOAuthParams(OAuthRequest request, String tokenSecret) { - final OAuthConfig config = getConfig(); request.addOAuthParameter(OAuthConstants.TIMESTAMP, api.getTimestampService().getTimestampInSeconds()); request.addOAuthParameter(OAuthConstants.NONCE, api.getTimestampService().getNonce()); - request.addOAuthParameter(OAuthConstants.CONSUMER_KEY, config.getApiKey()); + request.addOAuthParameter(OAuthConstants.CONSUMER_KEY, getApiKey()); request.addOAuthParameter(OAuthConstants.SIGN_METHOD, api.getSignatureService().getSignatureMethod()); request.addOAuthParameter(OAuthConstants.VERSION, getVersion()); - final String scope = config.getScope(); if (scope != null) { request.addOAuthParameter(OAuthConstants.SCOPE, scope); } request.addOAuthParameter(OAuthConstants.SIGNATURE, getSignature(request, tokenSecret)); - config.log("appended additional OAuth parameters: " + MapUtils.toString(request.getOauthParameters())); + if (isDebug()) { + log("appended additional OAuth parameters: %s", request.getOauthParameters()); + } } - public final OAuth1AccessToken getAccessToken(OAuth1RequestToken requestToken, String oauthVerifier) + public OAuth1AccessToken getAccessToken(OAuth1RequestToken requestToken, String oauthVerifier) throws IOException, InterruptedException, ExecutionException { - getConfig().log("obtaining access token from " + api.getAccessTokenEndpoint()); + if (isDebug()) { + log("obtaining access token from %s", api.getAccessTokenEndpoint()); + } final OAuthRequest request = prepareAccessTokenRequest(requestToken, oauthVerifier); - final Response response = execute(request); - return api.getAccessTokenExtractor().extract(response); + try ( Response response = execute(request)) { + return api.getAccessTokenExtractor().extract(response); + } } - public final Future getAccessTokenAsync(OAuth1RequestToken requestToken, String oauthVerifier) { + public Future getAccessTokenAsync(OAuth1RequestToken requestToken, String oauthVerifier) { return getAccessTokenAsync(requestToken, oauthVerifier, null); } /** - * Start the request to retrieve the access token. The optionally provided - * callback will be called with the Token when it is available. + * Start the request to retrieve the access token. The optionally provided callback will be called with the Token + * when it is available. * * @param requestToken request token (obtained previously or null) * @param oauthVerifier oauth_verifier * @param callback optional callback * @return Future */ - public final Future getAccessTokenAsync(OAuth1RequestToken requestToken, String oauthVerifier, + public Future getAccessTokenAsync(OAuth1RequestToken requestToken, String oauthVerifier, OAuthAsyncRequestCallback callback) { - final OAuthConfig config = getConfig(); - config.log("async obtaining access token from " + api.getAccessTokenEndpoint()); + if (isDebug()) { + log("async obtaining access token from %s", api.getAccessTokenEndpoint()); + } final OAuthRequest request = prepareAccessTokenRequest(requestToken, oauthVerifier); return execute(request, callback, new OAuthRequest.ResponseConverter() { @Override public OAuth1AccessToken convert(Response response) throws IOException { - return getApi().getAccessTokenExtractor().extract(response); + final OAuth1AccessToken token = getApi().getAccessTokenExtractor().extract(response); + response.close(); + return token; } }); } protected OAuthRequest prepareAccessTokenRequest(OAuth1RequestToken requestToken, String oauthVerifier) { final OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint()); - final OAuthConfig config = getConfig(); request.addOAuthParameter(OAuthConstants.TOKEN, requestToken.getToken()); request.addOAuthParameter(OAuthConstants.VERIFIER, oauthVerifier); - config.log("setting token to: " + requestToken + " and verifier to: " + oauthVerifier); + if (isDebug()) { + log("setting token to: %s and verifier to: %s", requestToken, oauthVerifier); + } addOAuthParams(request, requestToken.getTokenSecret()); appendSignature(request); return request; } - @Override public void signRequest(OAuth1AccessToken token, OAuthRequest request) { - final OAuthConfig config = getConfig(); - config.log("signing request: " + request.getCompleteUrl()); + if (isDebug()) { + log("signing request: %s", request.getCompleteUrl()); + } if (!token.isEmpty() || api.isEmptyOAuthTokenParamIsRequired()) { request.addOAuthParameter(OAuthConstants.TOKEN, token.getToken()); } - config.log("setting token to: " + token); + if (isDebug()) { + log("setting token to: %s", token); + } addOAuthParams(request, token.getTokenSecret()); appendSignature(request); } @@ -156,8 +172,7 @@ public String getVersion() { } /** - * Returns the URL where you should redirect your users to authenticate your - * application. + * Returns the URL where you should redirect your users to authenticate your application. * * @param requestToken the request token you need to authorize * @return the URL where you should redirect your users @@ -167,32 +182,31 @@ public String getAuthorizationUrl(OAuth1RequestToken requestToken) { } private String getSignature(OAuthRequest request, String tokenSecret) { - final OAuthConfig config = getConfig(); - config.log("generating signature..."); - config.log("using base64 encoder: " + Base64Encoder.type()); + log("generating signature..."); final String baseString = api.getBaseStringExtractor().extract(request); - final String signature = api.getSignatureService().getSignature(baseString, config.getApiSecret(), tokenSecret); + final String signature = api.getSignatureService().getSignature(baseString, getApiSecret(), tokenSecret); - config.log("base string is: " + baseString); - config.log("signature is: " + signature); + if (isDebug()) { + log("base string is: %s", baseString); + log("signature is: %s", signature); + } return signature; } protected void appendSignature(OAuthRequest request) { - final OAuthConfig config = getConfig(); final OAuth1SignatureType signatureType = api.getSignatureType(); switch (signatureType) { - case Header: - config.log("using Http Header signature"); + case HEADER: + log("using Http Header signature"); final String oauthHeader = api.getHeaderExtractor().extract(request); request.addHeader(OAuthConstants.HEADER, oauthHeader); break; - case QueryString: - config.log("using Querystring signature"); + case QUERY_STRING: + log("using Querystring signature"); - for (Map.Entry entry : request.getOauthParameters().entrySet()) { - request.addQuerystringParameter(entry.getKey(), entry.getValue()); + for (Map.Entry oauthParameter : request.getOauthParameters().entrySet()) { + request.addQuerystringParameter(oauthParameter.getKey(), oauthParameter.getValue()); } break; default: 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 4565a4165..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 @@ -1,40 +1,161 @@ package com.github.scribejava.core.oauth; -import com.github.scribejava.core.services.Base64Encoder; -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.concurrent.Future; import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; +import com.github.scribejava.core.model.DeviceAuthorization; import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.model.OAuth2AccessTokenErrorResponse; import com.github.scribejava.core.model.OAuth2Authorization; import com.github.scribejava.core.model.OAuthAsyncRequestCallback; -import com.github.scribejava.core.model.OAuthConfig; import com.github.scribejava.core.model.OAuthConstants; 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.oauth2.OAuth2Error; +import com.github.scribejava.core.pkce.PKCE; +import com.github.scribejava.core.revoke.TokenTypeHint; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; import java.util.Map; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; -public class OAuth20Service extends OAuthService { +public class OAuth20Service extends OAuthService { private static final String VERSION = "2.0"; private final DefaultApi20 api; + private final String responseType; + private final String defaultScope; + + public OAuth20Service(DefaultApi20 api, String apiKey, String apiSecret, String callback, String defaultScope, + String responseType, OutputStream debugStream, String userAgent, HttpClientConfig httpClientConfig, + HttpClient httpClient) { + super(apiKey, apiSecret, callback, debugStream, userAgent, httpClientConfig, httpClient); + this.responseType = responseType; + this.api = api; + this.defaultScope = defaultScope; + } + // ===== common OAuth methods ===== /** - * Default constructor + * {@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. * - * @param api OAuth2.0 api information - * @param config OAuth 2.0 configuration param object + * @return the URL where you should redirect your users */ - public OAuth20Service(DefaultApi20 api, OAuthConfig config) { - super(config); - this.api = api; + 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 { - return api.getAccessTokenExtractor().extract(execute(request)); + if (isDebug()) { + log("send request for access token synchronously to %s", request.getCompleteUrl()); + } + try (Response response = execute(request)) { + if (isDebug()) { + log("response status code: %s", response.getCode()); + log("response body: %s", response.getBody()); + } + + return api.getAccessTokenExtractor().extract(response); + } } //protected to facilitate mocking @@ -45,200 +166,511 @@ protected Future sendAccessTokenRequestAsync(OAuthRequest req //protected to facilitate mocking protected Future sendAccessTokenRequestAsync(OAuthRequest request, OAuthAsyncRequestCallback callback) { + if (isDebug()) { + log("send request for access token asynchronously to %s", request.getCompleteUrl()); + } return execute(request, callback, new OAuthRequest.ResponseConverter() { @Override public OAuth2AccessToken convert(Response response) throws IOException { - return getApi().getAccessTokenExtractor().extract(response); + log("received response for access token"); + if (isDebug()) { + log("response status code: %s", response.getCode()); + log("response body: %s", response.getBody()); + } + final OAuth2AccessToken token = api.getAccessTokenExtractor().extract(response); + response.close(); + return token; } }); } - public final Future getAccessTokenAsync(String code) { - return getAccessToken(code, null); + // ===== 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); } - public final OAuth2AccessToken getAccessToken(String code) - throws IOException, InterruptedException, ExecutionException { - final OAuthRequest request = createAccessTokenRequest(code); + public Future getAccessTokenAsync(AccessTokenRequestParams params) { + return getAccessToken(params, null); + } - return sendAccessTokenRequestSync(request); + public OAuth2AccessToken getAccessToken(String code) throws IOException, InterruptedException, ExecutionException { + return getAccessToken(AccessTokenRequestParams.create(code)); + } + + public OAuth2AccessToken getAccessToken(AccessTokenRequestParams params) + throws IOException, InterruptedException, ExecutionException { + return sendAccessTokenRequestSync(createAccessTokenRequest(params)); } /** * Start the request to retrieve the access token. The optionally provided callback will be called with the Token * when it is available. * - * @param code code + * @param params params * @param callback optional callback * @return Future */ - public final Future getAccessToken(String code, + public Future getAccessToken(AccessTokenRequestParams params, OAuthAsyncRequestCallback callback) { - final OAuthRequest request = createAccessTokenRequest(code); + return sendAccessTokenRequestAsync(createAccessTokenRequest(params), callback); + } - return sendAccessTokenRequestAsync(request, callback); + public Future getAccessToken(String code, + OAuthAsyncRequestCallback callback) { + return getAccessToken(AccessTokenRequestParams.create(code), callback); } - protected OAuthRequest createAccessTokenRequest(String code) { - final OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint()); - final OAuthConfig config = getConfig(); - request.addParameter(OAuthConstants.CLIENT_ID, config.getApiKey()); - final String apiSecret = config.getApiSecret(); - if (apiSecret != null) { - request.addParameter(OAuthConstants.CLIENT_SECRET, apiSecret); - } - request.addParameter(OAuthConstants.CODE, code); - request.addParameter(OAuthConstants.REDIRECT_URI, config.getCallback()); - final String scope = config.getScope(); + // ===== 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()); + 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); + + request.addParameter(OAuthConstants.REFRESH_TOKEN, refreshToken); + request.addParameter(OAuthConstants.GRANT_TYPE, OAuthConstants.REFRESH_TOKEN); + + logRequestWithParams("refresh token", request); + return request; } - public final Future refreshAccessTokenAsync(String refreshToken) { - return refreshAccessToken(refreshToken, null); + public Future refreshAccessTokenAsync(String refreshToken) { + return refreshAccessToken(refreshToken, (OAuthAsyncRequestCallback) null); + } + + public Future refreshAccessTokenAsync(String refreshToken, String scope) { + return refreshAccessToken(refreshToken, scope, null); } - public final OAuth2AccessToken refreshAccessToken(String refreshToken) + public OAuth2AccessToken refreshAccessToken(String refreshToken) throws IOException, InterruptedException, ExecutionException { - final OAuthRequest request = createRefreshTokenRequest(refreshToken); + return refreshAccessToken(refreshToken, (String) null); + } + + public OAuth2AccessToken refreshAccessToken(String refreshToken, String scope) + throws IOException, InterruptedException, ExecutionException { + final OAuthRequest request = createRefreshTokenRequest(refreshToken, scope); return sendAccessTokenRequestSync(request); } - public final Future refreshAccessToken(String refreshToken, + public Future refreshAccessToken(String refreshToken, OAuthAsyncRequestCallback callback) { - final OAuthRequest request = createRefreshTokenRequest(refreshToken); + final OAuthRequest request = createRefreshTokenRequest(refreshToken, null); return sendAccessTokenRequestAsync(request, callback); } - protected OAuthRequest createRefreshTokenRequest(String refreshToken) { - if (refreshToken == null || refreshToken.isEmpty()) { - throw new IllegalArgumentException("The refreshToken cannot be null or empty"); - } - final OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getRefreshTokenEndpoint()); - final OAuthConfig config = getConfig(); - request.addParameter(OAuthConstants.CLIENT_ID, config.getApiKey()); - final String apiSecret = config.getApiSecret(); - if (apiSecret != null) { - request.addParameter(OAuthConstants.CLIENT_SECRET, apiSecret); + public Future refreshAccessToken(String refreshToken, String scope, + OAuthAsyncRequestCallback callback) { + final OAuthRequest request = createRefreshTokenRequest(refreshToken, scope); + + return sendAccessTokenRequestAsync(request, callback); + } + + // ===== 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); + } else if (defaultScope != null) { + 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); + + api.getClientAuthentication().addClientAuthentication(request, getApiKey(), getApiSecret()); + + logRequestWithParams("access token password grant", request); + return request; } - public final OAuth2AccessToken getAccessTokenPasswordGrant(String uname, String password) + public OAuth2AccessToken getAccessTokenPasswordGrant(String username, String password) + throws IOException, InterruptedException, ExecutionException { + final OAuthRequest request = createAccessTokenPasswordGrantRequest(username, password, null); + + return sendAccessTokenRequestSync(request); + } + + public OAuth2AccessToken getAccessTokenPasswordGrant(String username, String password, String scope) throws IOException, InterruptedException, ExecutionException { - final OAuthRequest request = createAccessTokenPasswordGrantRequest(uname, password); + final OAuthRequest request = createAccessTokenPasswordGrantRequest(username, password, scope); return sendAccessTokenRequestSync(request); } - public final Future getAccessTokenPasswordGrantAsync(String uname, String password) { - return getAccessTokenPasswordGrantAsync(uname, password, null); + public Future getAccessTokenPasswordGrantAsync(String username, String password) { + return getAccessTokenPasswordGrantAsync(username, password, + (OAuthAsyncRequestCallback) null); + } + + public Future getAccessTokenPasswordGrantAsync(String username, String password, String scope) { + return getAccessTokenPasswordGrantAsync(username, password, scope, null); } /** * Request Access Token Password Grant async version * - * @param uname User name + * @param username User name * @param password User password * @param callback Optional callback * @return Future */ - public final Future getAccessTokenPasswordGrantAsync(String uname, String password, + public Future getAccessTokenPasswordGrantAsync(String username, String password, OAuthAsyncRequestCallback callback) { - final OAuthRequest request = createAccessTokenPasswordGrantRequest(uname, password); + final OAuthRequest request = createAccessTokenPasswordGrantRequest(username, password, null); return sendAccessTokenRequestAsync(request, callback); } - protected OAuthRequest createAccessTokenPasswordGrantRequest(String username, String password) { + public Future getAccessTokenPasswordGrantAsync(String username, String password, String scope, + OAuthAsyncRequestCallback callback) { + final OAuthRequest request = createAccessTokenPasswordGrantRequest(username, password, scope); + + return sendAccessTokenRequestAsync(request, callback); + } + + // ===== get AccessToken client credentials flow methods ===== + protected OAuthRequest createAccessTokenClientCredentialsGrantRequest(String scope) { final OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint()); - final OAuthConfig config = getConfig(); - request.addParameter(OAuthConstants.USERNAME, username); - request.addParameter(OAuthConstants.PASSWORD, password); - final String scope = config.getScope(); + 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); - - final String apiKey = config.getApiKey(); - final String apiSecret = config.getApiSecret(); - if (apiKey != null && apiSecret != null) { - request.addHeader(OAuthConstants.HEADER, - OAuthConstants.BASIC + ' ' - + Base64Encoder.getInstance() - .encode(String.format("%s:%s", apiKey, apiSecret).getBytes(Charset.forName("UTF-8")))); - } + logRequestWithParams("access token client credentials grant", request); return request; } + public Future getAccessTokenClientCredentialsGrantAsync() { + return getAccessTokenClientCredentialsGrant((OAuthAsyncRequestCallback) null); + } + + public Future getAccessTokenClientCredentialsGrantAsync(String scope) { + return getAccessTokenClientCredentialsGrant(scope, null); + } + + public OAuth2AccessToken getAccessTokenClientCredentialsGrant() + throws IOException, InterruptedException, ExecutionException { + final OAuthRequest request = createAccessTokenClientCredentialsGrantRequest(null); + + return sendAccessTokenRequestSync(request); + } + + public OAuth2AccessToken getAccessTokenClientCredentialsGrant(String scope) + throws IOException, InterruptedException, ExecutionException { + final OAuthRequest request = createAccessTokenClientCredentialsGrantRequest(scope); + + return sendAccessTokenRequestSync(request); + } + /** - * {@inheritDoc} + * Start the request to retrieve the access token using client-credentials grant. The optionally provided callback + * will be called with the Token when it is available. + * + * @param callback optional callback + * @return Future */ - @Override - public String getVersion() { - return VERSION; + public Future getAccessTokenClientCredentialsGrant( + OAuthAsyncRequestCallback callback) { + final OAuthRequest request = createAccessTokenClientCredentialsGrantRequest(null); + + return sendAccessTokenRequestAsync(request, callback); } - @Override - public void signRequest(OAuth2AccessToken accessToken, OAuthRequest request) { - api.getSignatureType().signRequest(accessToken, request); + public Future getAccessTokenClientCredentialsGrant(String scope, + OAuthAsyncRequestCallback callback) { + final OAuthRequest request = createAccessTokenClientCredentialsGrantRequest(scope); + + return sendAccessTokenRequestAsync(request, callback); + } + + // ===== revoke AccessToken methods ===== + protected OAuthRequest createRevokeTokenRequest(String tokenToRevoke, TokenTypeHint tokenTypeHint) { + final OAuthRequest request = new OAuthRequest(Verb.POST, api.getRevokeTokenEndpoint()); + + api.getClientAuthentication().addClientAuthentication(request, getApiKey(), getApiSecret()); + + request.addParameter("token", tokenToRevoke); + if (tokenTypeHint != null) { + request.addParameter("token_type_hint", tokenTypeHint.getValue()); + } + + logRequestWithParams("revoke token", request); + + return request; + } + + public Future revokeTokenAsync(String tokenToRevoke) { + return revokeTokenAsync(tokenToRevoke, null); + } + + public Future revokeTokenAsync(String tokenToRevoke, TokenTypeHint tokenTypeHint) { + return revokeToken(tokenToRevoke, null, tokenTypeHint); + } + + public void revokeToken(String tokenToRevoke) throws IOException, InterruptedException, ExecutionException { + revokeToken(tokenToRevoke, (TokenTypeHint) null); + } + + public void revokeToken(String tokenToRevoke, TokenTypeHint tokenTypeHint) + throws IOException, InterruptedException, ExecutionException { + final OAuthRequest request = createRevokeTokenRequest(tokenToRevoke, tokenTypeHint); + + try (Response response = execute(request)) { + checkForErrorRevokeToken(response); + } + } + + public Future revokeToken(String tokenToRevoke, OAuthAsyncRequestCallback callback) { + return revokeToken(tokenToRevoke, callback, null); + } + + public Future revokeToken(String tokenToRevoke, OAuthAsyncRequestCallback callback, + TokenTypeHint tokenTypeHint) { + final OAuthRequest request = createRevokeTokenRequest(tokenToRevoke, tokenTypeHint); + + return execute(request, callback, new OAuthRequest.ResponseConverter() { + @Override + public Void convert(Response response) throws IOException { + checkForErrorRevokeToken(response); + response.close(); + return null; + } + }); + } + + private void checkForErrorRevokeToken(Response response) throws IOException { + if (response.getCode() != 200) { + OAuth2AccessTokenJsonExtractor.instance().generateError(response); + } + } + + // ===== device Authorisation codes methods ===== + protected OAuthRequest createDeviceAuthorizationCodesRequest(String scope) { + final OAuthRequest request = new OAuthRequest(Verb.POST, api.getDeviceAuthorizationEndpoint()); + request.addParameter(OAuthConstants.CLIENT_ID, getApiKey()); + if (scope != null) { + request.addParameter(OAuthConstants.SCOPE, scope); + } else if (defaultScope != null) { + request.addParameter(OAuthConstants.SCOPE, defaultScope); + } + + logRequestWithParams("Device Authorization Codes", request); + + return request; } /** - * Returns the URL where you should redirect your users to authenticate your application. + * Requests a set of verification codes from the authorization server with the default scope * - * @return the URL where you should redirect your users + * @see RFC 8628 + * + * @return DeviceAuthorization + * @throws InterruptedException InterruptedException + * @throws ExecutionException ExecutionException + * @throws IOException IOException */ - public final String getAuthorizationUrl() { - return getAuthorizationUrl(null); + public DeviceAuthorization getDeviceAuthorizationCodes() + throws InterruptedException, ExecutionException, IOException { + return getDeviceAuthorizationCodes((String) null); } /** - * Returns the URL where you should redirect your users to authenticate your application. + * Requests a set of verification codes from the authorization server * - * @param additionalParams any additional GET params to add to the URL - * @return the URL where you should redirect your users + * @see RFC 8628 + * + * @param scope scope + * @return DeviceAuthorization + * @throws InterruptedException InterruptedException + * @throws ExecutionException ExecutionException + * @throws IOException IOException */ - public String getAuthorizationUrl(Map additionalParams) { - return api.getAuthorizationUrl(getConfig(), additionalParams); + public DeviceAuthorization getDeviceAuthorizationCodes(String scope) + throws InterruptedException, ExecutionException, IOException { + final OAuthRequest request = createDeviceAuthorizationCodesRequest(scope); + + try (Response response = execute(request)) { + if (isDebug()) { + log("got DeviceAuthorizationCodes response"); + log("response status code: %s", response.getCode()); + log("response body: %s", response.getBody()); + } + return api.getDeviceAuthorizationExtractor().extract(response); + } } - public DefaultApi20 getApi() { - return api; + public Future getDeviceAuthorizationCodes( + OAuthAsyncRequestCallback callback) { + return getDeviceAuthorizationCodes(null, callback); } - public OAuth2Authorization extractAuthorization(String redirectLocation) { - final OAuth2Authorization authorization = new OAuth2Authorization(); - int end = redirectLocation.indexOf('#'); - if (end == -1) { - end = redirectLocation.length(); + public Future getDeviceAuthorizationCodes(String scope, + OAuthAsyncRequestCallback callback) { + final OAuthRequest request = createDeviceAuthorizationCodesRequest(scope); + + return execute(request, callback, new OAuthRequest.ResponseConverter() { + @Override + public DeviceAuthorization convert(Response response) throws IOException { + final DeviceAuthorization deviceAuthorization = api.getDeviceAuthorizationExtractor().extract(response); + response.close(); + return deviceAuthorization; + } + }); + } + + public Future getDeviceAuthorizationCodesAsync() { + return getDeviceAuthorizationCodesAsync(null); + } + + 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"); + request.addParameter("device_code", deviceAuthorization.getDeviceCode()); + api.getClientAuthentication().addClientAuthentication(request, getApiKey(), getApiSecret()); + return request; + } + + /** + * Attempts to get a token from a server. + * + * Function {@link #pollAccessTokenDeviceAuthorizationGrant(com.github.scribejava.core.model.DeviceAuthorization)} + * is usually used instead of this. + * + * @param deviceAuthorization deviceAuthorization + * @return token + * + * @throws java.lang.InterruptedException InterruptedException + * @throws java.util.concurrent.ExecutionException ExecutionException + * @throws java.io.IOException IOException + * @throws OAuth2AccessTokenErrorResponse If {@link OAuth2AccessTokenErrorResponse#getError()} is + * {@link com.github.scribejava.core.oauth2.OAuth2Error#AUTHORIZATION_PENDING} or + * {@link com.github.scribejava.core.oauth2.OAuth2Error#SLOW_DOWN}, another attempt should be made after a while. + * + * @see #getDeviceAuthorizationCodes() + */ + public OAuth2AccessToken getAccessTokenDeviceAuthorizationGrant(DeviceAuthorization deviceAuthorization) + throws InterruptedException, ExecutionException, IOException { + final OAuthRequest request = createAccessTokenDeviceAuthorizationGrantRequest(deviceAuthorization); + + try (Response response = execute(request)) { + if (isDebug()) { + log("got AccessTokenDeviceAuthorizationGrant response"); + log("response status code: %s", response.getCode()); + log("response body: %s", response.getBody()); + } + return api.getAccessTokenExtractor().extract(response); } - for (String param : redirectLocation.substring(redirectLocation.indexOf('?') + 1, end).split("&")) { - final String[] keyValue = param.split("="); - if (keyValue.length == 2) { - switch (keyValue[0]) { - case "code": - authorization.setCode(keyValue[1]); - break; - case "state": - authorization.setState(keyValue[1]); - break; - default: //just ignore any other param; + } + + public Future getAccessTokenDeviceAuthorizationGrant(DeviceAuthorization deviceAuthorization, + OAuthAsyncRequestCallback callback) { + final OAuthRequest request = createAccessTokenDeviceAuthorizationGrantRequest(deviceAuthorization); + + return execute(request, callback, new OAuthRequest.ResponseConverter() { + @Override + public OAuth2AccessToken convert(Response response) throws IOException { + final OAuth2AccessToken accessToken = api.getAccessTokenExtractor().extract(response); + response.close(); + return accessToken; + } + }); + } + + public Future getAccessTokenDeviceAuthorizationGrantAsync( + DeviceAuthorization deviceAuthorization) { + return getAccessTokenDeviceAuthorizationGrant(deviceAuthorization, null); + } + + /** + * Periodically tries to get a token from a server (waiting for the user to give consent). Sync only version. No + * Async variants yet, one should implement async scenarios themselves. + * + * @param deviceAuthorization deviceAuthorization + * @return token + * @throws java.lang.InterruptedException InterruptedException + * @throws java.util.concurrent.ExecutionException ExecutionException + * @throws java.io.IOException IOException + * @throws OAuth2AccessTokenErrorResponse Indicates OAuth error. + * + * @see #getDeviceAuthorizationCodes() + */ + public OAuth2AccessToken pollAccessTokenDeviceAuthorizationGrant(DeviceAuthorization deviceAuthorization) + throws InterruptedException, ExecutionException, IOException { + long intervalMillis = deviceAuthorization.getIntervalSeconds() * 1000; + while (true) { + try { + return getAccessTokenDeviceAuthorizationGrant(deviceAuthorization); + } catch (OAuth2AccessTokenErrorResponse e) { + if (e.getError() != OAuth2Error.AUTHORIZATION_PENDING) { + if (e.getError() == OAuth2Error.SLOW_DOWN) { + intervalMillis += 5000; + } else { + throw e; + } } } + Thread.sleep(intervalMillis); } - return authorization; } } diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/oauth/OAuthService.java b/scribejava-core/src/main/java/com/github/scribejava/core/oauth/OAuthService.java index df58ce3ea..996abee6f 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/oauth/OAuthService.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/oauth/OAuthService.java @@ -6,37 +6,38 @@ import com.github.scribejava.core.httpclient.jdk.JDKHttpClient; import com.github.scribejava.core.httpclient.jdk.JDKHttpClientConfig; import com.github.scribejava.core.model.OAuthAsyncRequestCallback; -import com.github.scribejava.core.model.OAuthConfig; import com.github.scribejava.core.model.OAuthRequest; import com.github.scribejava.core.model.Response; -import com.github.scribejava.core.model.Token; +import java.io.Closeable; import java.io.File; import java.io.IOException; +import java.io.OutputStream; import java.util.ServiceLoader; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; -/** - * The main ScribeJava object. - * - * A facade responsible for the retrieval of request and access tokens and for the signing of HTTP requests. - * @param type of token used to sign the request - */ -public abstract class OAuthService implements AutoCloseable { +public abstract class OAuthService implements Closeable { - private final OAuthConfig config; + private final String apiKey; + private final String apiSecret; + private final String callback; + private final String userAgent; private final HttpClient httpClient; - - public OAuthService(OAuthConfig config) { - this.config = config; - final HttpClientConfig httpClientConfig = config.getHttpClientConfig(); - final HttpClient externalHttpClient = config.getHttpClient(); - - if (httpClientConfig == null && externalHttpClient == null) { - httpClient = new JDKHttpClient(JDKHttpClientConfig.defaultConfig()); + private final OutputStream debugStream; + + public OAuthService(String apiKey, String apiSecret, String callback, OutputStream debugStream, + String userAgent, HttpClientConfig httpClientConfig, HttpClient httpClient) { + this.apiKey = apiKey; + this.apiSecret = apiSecret; + this.callback = callback; + this.debugStream = debugStream; + this.userAgent = userAgent; + + if (httpClientConfig == null && httpClient == null) { + this.httpClient = new JDKHttpClient(JDKHttpClientConfig.defaultConfig()); } else { - httpClient = externalHttpClient == null ? getClient(httpClientConfig) : externalHttpClient; + this.httpClient = httpClient == null ? getClient(httpClientConfig) : httpClient; } } @@ -55,8 +56,16 @@ public void close() throws IOException { httpClient.close(); } - public OAuthConfig getConfig() { - return config; + public String getApiKey() { + return apiKey; + } + + public String getApiSecret() { + return apiSecret; + } + + public String getCallback() { + return callback; } /** @@ -66,8 +75,6 @@ public OAuthConfig getConfig() { */ public abstract String getVersion(); - public abstract void signRequest(T token, OAuthRequest request); - public Future executeAsync(OAuthRequest request) { return execute(request, null); } @@ -81,28 +88,61 @@ public Future execute(OAuthRequest request, OAuthAsyncRequestCallback final File filePayload = request.getFilePayload(); if (filePayload != null) { - return httpClient.executeAsync(config.getUserAgent(), request.getHeaders(), request.getVerb(), - request.getCompleteUrl(), filePayload, callback, converter); + return httpClient.executeAsync(userAgent, request.getHeaders(), request.getVerb(), request.getCompleteUrl(), + filePayload, callback, converter); } else if (request.getStringPayload() != null) { - return httpClient.executeAsync(config.getUserAgent(), request.getHeaders(), request.getVerb(), - request.getCompleteUrl(), request.getStringPayload(), callback, converter); + return httpClient.executeAsync(userAgent, request.getHeaders(), request.getVerb(), request.getCompleteUrl(), + request.getStringPayload(), callback, converter); } else { - return httpClient.executeAsync(config.getUserAgent(), request.getHeaders(), request.getVerb(), - request.getCompleteUrl(), request.getByteArrayPayload(), callback, converter); + return httpClient.executeAsync(userAgent, request.getHeaders(), request.getVerb(), request.getCompleteUrl(), + request.getByteArrayPayload(), callback, converter); } } public Response execute(OAuthRequest request) throws InterruptedException, ExecutionException, IOException { final File filePayload = request.getFilePayload(); if (filePayload != null) { - return httpClient.execute(config.getUserAgent(), request.getHeaders(), request.getVerb(), - request.getCompleteUrl(), filePayload); + return httpClient.execute(userAgent, request.getHeaders(), request.getVerb(), request.getCompleteUrl(), + filePayload); } else if (request.getStringPayload() != null) { - return httpClient.execute(config.getUserAgent(), request.getHeaders(), request.getVerb(), - request.getCompleteUrl(), request.getStringPayload()); + return httpClient.execute(userAgent, request.getHeaders(), request.getVerb(), request.getCompleteUrl(), + request.getStringPayload()); + } else if (request.getMultipartPayload() != null) { + return httpClient.execute(userAgent, request.getHeaders(), request.getVerb(), request.getCompleteUrl(), + request.getMultipartPayload()); } else { - return httpClient.execute(config.getUserAgent(), request.getHeaders(), request.getVerb(), - request.getCompleteUrl(), request.getByteArrayPayload()); + return httpClient.execute(userAgent, request.getHeaders(), request.getVerb(), request.getCompleteUrl(), + request.getByteArrayPayload()); } } + + /** + * No need to wrap usages in {@link #isDebug()}. + * + * @param message message to log + */ + public void log(String message) { + if (debugStream != null) { + log(message, (Object[]) null); + } + } + + /** + * Wrap usages in {@link #isDebug()}. It was made for optimization - to not calculate "params" in production mode. + * + * @param messagePattern messagePattern + * @param params params + */ + public void log(String messagePattern, Object... params) { + final String message = String.format(messagePattern, params) + '\n'; + try { + debugStream.write(message.getBytes("UTF8")); + } catch (IOException | RuntimeException e) { + throw new RuntimeException("there were problems while writting to the debug stream", e); + } + } + + protected boolean isDebug() { + return debugStream != null; + } } diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/oauth2/OAuth2Error.java b/scribejava-core/src/main/java/com/github/scribejava/core/oauth2/OAuth2Error.java new file mode 100644 index 000000000..b1422bcae --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/oauth2/OAuth2Error.java @@ -0,0 +1,108 @@ +package com.github.scribejava.core.oauth2; + +import java.util.EnumSet; +import java.util.Set; + +public enum OAuth2Error { + /** + * @see RFC 6749, 4.1.2.1 Error Response + * @see RFC 6749, 4.2.2.1 Error Response + * @see RFC 6749, 5.2 Error Response + * @see RFC 6750, 6.2. OAuth Extensions Error + * Registration + */ + INVALID_REQUEST("invalid_request"), + /** + * @see RFC 6749, 4.1.2.1 Error Response + * @see RFC 6749, 4.2.2.1 Error Response + * @see RFC 6749, 5.2 Error Response + */ + UNAUTHORIZED_CLIENT("unauthorized_client"), + /** + * @see RFC 6749, 4.1.2.1 Error Response + * @see RFC 6749, 4.2.2.1 Error Response + * @see RFC 8628, 3.5. Device Access Token Response + */ + ACCESS_DENIED("access_denied"), + /** + * @see RFC 6749, 4.1.2.1 Error Response + * @see RFC 6749, 4.2.2.1 Error Response + */ + UNSUPPORTED_RESPONSE_TYPE("unsupported_response_type"), + /** + * @see RFC 6749, 4.1.2.1 Error Response + * @see RFC 6749, 4.2.2.1 Error Response + * @see RFC 6749, 5.2 Error Response + */ + INVALID_SCOPE("invalid_scope"), + /** + * @see RFC 6749, 4.1.2.1 Error Response + * @see RFC 6749, 4.2.2.1 Error Response + */ + SERVER_ERROR("server_error"), + /** + * @see RFC 6749, 4.1.2.1 Error Response + * @see RFC 6749, 4.2.2.1 Error Response + */ + TEMPORARILY_UNAVAILABLE("temporarily_unavailable"), + /** + * @see RFC 6749, 5.2 Error Response + */ + INVALID_CLIENT("invalid_client"), + /** + * @see RFC 6749, 5.2 Error Response + */ + INVALID_GRANT("invalid_grant"), + /** + * @see RFC 6749, 5.2 Error Response + */ + UNSUPPORTED_GRANT_TYPE("unsupported_grant_type"), + /** + * @see RFC 6750, 6.2. OAuth Extensions Error + * Registration + */ + INVALID_TOKEN("invalid_token"), + /** + * @see RFC 6750, 6.2. OAuth Extensions Error + * Registration + */ + INSUFFICIENT_SCOPE("insufficient_scope"), + /** + * @see RFC 7009, 4.1. OAuth Extensions Error + * Registration + */ + UNSUPPORTED_TOKEN_TYPE("unsupported_token_type"), + /** + * @see RFC 8628, 3.5. Device Access Token Response + */ + AUTHORIZATION_PENDING("authorization_pending"), + /** + * @see RFC 8628, 3.5. Device Access Token Response + */ + SLOW_DOWN("slow_down"), + /** + * @see RFC 8628, 3.5. Device Access Token Response + */ + EXPIRED_TOKEN("expired_token"); + + private static final Set VALUES = EnumSet.allOf(OAuth2Error.class); + + private final String errorString; + + OAuth2Error(String errorString) { + this.errorString = errorString; + } + + public static OAuth2Error parseFrom(String errorString) { + for (OAuth2Error error : VALUES) { + if (error.errorString.equals(errorString)) { + return error; + } + } + throw new IllegalArgumentException("there is no knowlege about '" + errorString + "' Error"); + } + + public String getErrorString() { + return errorString; + } +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/oauth2/bearersignature/BearerSignature.java b/scribejava-core/src/main/java/com/github/scribejava/core/oauth2/bearersignature/BearerSignature.java new file mode 100644 index 000000000..61739e93e --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/oauth2/bearersignature/BearerSignature.java @@ -0,0 +1,12 @@ +package com.github.scribejava.core.oauth2.bearersignature; + +import com.github.scribejava.core.model.OAuthRequest; + +/** + * Represents
+ * 2. Authenticated Requests
+ * https://tools.ietf.org/html/rfc6750#section-2 + */ +public interface BearerSignature { + void signRequest(String accessToken, OAuthRequest request); +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/oauth2/bearersignature/BearerSignatureAuthorizationRequestHeaderField.java b/scribejava-core/src/main/java/com/github/scribejava/core/oauth2/bearersignature/BearerSignatureAuthorizationRequestHeaderField.java new file mode 100644 index 000000000..98196a708 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/oauth2/bearersignature/BearerSignatureAuthorizationRequestHeaderField.java @@ -0,0 +1,29 @@ +package com.github.scribejava.core.oauth2.bearersignature; + +import com.github.scribejava.core.model.OAuthConstants; +import com.github.scribejava.core.model.OAuthRequest; + +/** + * 2.1. Authorization Request Header Field
+ * https://tools.ietf.org/html/rfc6750#section-2.1 + */ +public class BearerSignatureAuthorizationRequestHeaderField implements BearerSignature { + + protected BearerSignatureAuthorizationRequestHeaderField() { + } + + private static class InstanceHolder { + + private static final BearerSignatureAuthorizationRequestHeaderField INSTANCE + = new BearerSignatureAuthorizationRequestHeaderField(); + } + + public static BearerSignatureAuthorizationRequestHeaderField instance() { + return InstanceHolder.INSTANCE; + } + + @Override + public void signRequest(String accessToken, OAuthRequest request) { + request.addHeader(OAuthConstants.HEADER, "Bearer " + accessToken); + } +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/oauth2/bearersignature/BearerSignatureURIQueryParameter.java b/scribejava-core/src/main/java/com/github/scribejava/core/oauth2/bearersignature/BearerSignatureURIQueryParameter.java new file mode 100644 index 000000000..72cc64b43 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/oauth2/bearersignature/BearerSignatureURIQueryParameter.java @@ -0,0 +1,27 @@ +package com.github.scribejava.core.oauth2.bearersignature; + +import com.github.scribejava.core.model.OAuthConstants; +import com.github.scribejava.core.model.OAuthRequest; + +/** + * 2.3. URI Query Parameter
+ * https://tools.ietf.org/html/rfc6750#section-2.3 + */ +public class BearerSignatureURIQueryParameter implements BearerSignature { + protected BearerSignatureURIQueryParameter() { + } + + private static class InstanceHolder { + + private static final BearerSignatureURIQueryParameter INSTANCE = new BearerSignatureURIQueryParameter(); + } + + public static BearerSignatureURIQueryParameter instance() { + return InstanceHolder.INSTANCE; + } + + @Override + public void signRequest(String accessToken, OAuthRequest request) { + request.addQuerystringParameter(OAuthConstants.ACCESS_TOKEN, accessToken); + } +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/oauth2/clientauthentication/ClientAuthentication.java b/scribejava-core/src/main/java/com/github/scribejava/core/oauth2/clientauthentication/ClientAuthentication.java new file mode 100644 index 000000000..54fdb5de6 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/oauth2/clientauthentication/ClientAuthentication.java @@ -0,0 +1,17 @@ +package com.github.scribejava.core.oauth2.clientauthentication; + +import com.github.scribejava.core.model.OAuthRequest; + +/** + * Represents
+ * 2.3. Client Authentication
+ * https://tools.ietf.org/html/rfc6749#section-2.3 + *
+ * just implement this interface to implement "2.3.2. Other Authentication Methods"
+ * https://tools.ietf.org/html/rfc6749#section-2.3.2 + * + */ +public interface ClientAuthentication { + + void addClientAuthentication(OAuthRequest request, String apiKey, String apiSecret); +} 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 new file mode 100644 index 000000000..3931f6f7b --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/oauth2/clientauthentication/HttpBasicAuthenticationScheme.java @@ -0,0 +1,37 @@ +package com.github.scribejava.core.oauth2.clientauthentication; + +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; + +/** + * 2.3. Client Authentication
+ * 2.3.1. Client Password
+ * https://tools.ietf.org/html/rfc6749#section-2.3.1 + *
+ * НTTP Basic authentication scheme + */ +public class HttpBasicAuthenticationScheme implements ClientAuthentication { + + protected HttpBasicAuthenticationScheme() { + } + + private static class InstanceHolder { + + private static final HttpBasicAuthenticationScheme INSTANCE = new HttpBasicAuthenticationScheme(); + } + + public static HttpBasicAuthenticationScheme instance() { + return InstanceHolder.INSTANCE; + } + + @Override + public void addClientAuthentication(OAuthRequest request, String apiKey, String apiSecret) { + if (apiKey != null && apiSecret != null) { + request.addHeader(OAuthConstants.HEADER, OAuthConstants.BASIC + ' ' + + 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/oauth2/clientauthentication/RequestBodyAuthenticationScheme.java b/scribejava-core/src/main/java/com/github/scribejava/core/oauth2/clientauthentication/RequestBodyAuthenticationScheme.java new file mode 100644 index 000000000..5e91affa7 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/oauth2/clientauthentication/RequestBodyAuthenticationScheme.java @@ -0,0 +1,34 @@ +package com.github.scribejava.core.oauth2.clientauthentication; + +import com.github.scribejava.core.model.OAuthConstants; +import com.github.scribejava.core.model.OAuthRequest; + +/** + * 2.3. Client Authentication
+ * 2.3.1. Client Password
+ * https://tools.ietf.org/html/rfc6749#section-2.3.1 + *
+ * request-body authentication scheme + */ +public class RequestBodyAuthenticationScheme implements ClientAuthentication { + + protected RequestBodyAuthenticationScheme() { + } + + private static class InstanceHolder { + + private static final RequestBodyAuthenticationScheme INSTANCE = new RequestBodyAuthenticationScheme(); + } + + public static RequestBodyAuthenticationScheme instance() { + return InstanceHolder.INSTANCE; + } + + @Override + public void addClientAuthentication(OAuthRequest request, String apiKey, String apiSecret) { + request.addParameter(OAuthConstants.CLIENT_ID, apiKey); + if (apiSecret != null) { + request.addParameter(OAuthConstants.CLIENT_SECRET, apiSecret); + } + } +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/pkce/PKCE.java b/scribejava-core/src/main/java/com/github/scribejava/core/pkce/PKCE.java new file mode 100644 index 000000000..c8d43d26e --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/pkce/PKCE.java @@ -0,0 +1,49 @@ +package com.github.scribejava.core.pkce; + +import java.util.HashMap; +import java.util.Map; + +/** + * Used to hold code_challenge, code_challenge_method and code_verifier for https://tools.ietf.org/html/rfc7636 + */ +public class PKCE { + + public static final String PKCE_CODE_CHALLENGE_METHOD_PARAM = "code_challenge_method"; + public static final String PKCE_CODE_CHALLENGE_PARAM = "code_challenge"; + public static final String PKCE_CODE_VERIFIER_PARAM = "code_verifier"; + + private String codeChallenge; + private PKCECodeChallengeMethod codeChallengeMethod = PKCECodeChallengeMethod.S256; + private String codeVerifier; + + public String getCodeChallenge() { + return codeChallenge; + } + + public void setCodeChallenge(String codeChallenge) { + this.codeChallenge = codeChallenge; + } + + public PKCECodeChallengeMethod getCodeChallengeMethod() { + return codeChallengeMethod; + } + + public void setCodeChallengeMethod(PKCECodeChallengeMethod codeChallengeMethod) { + this.codeChallengeMethod = codeChallengeMethod; + } + + public String getCodeVerifier() { + return codeVerifier; + } + + public void setCodeVerifier(String codeVerifier) { + this.codeVerifier = codeVerifier; + } + + public Map getAuthorizationUrlParams() { + final Map params = new HashMap<>(); + params.put(PKCE_CODE_CHALLENGE_PARAM, codeChallenge); + params.put(PKCE_CODE_CHALLENGE_METHOD_PARAM, codeChallengeMethod.name()); + return params; + } +} 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 new file mode 100644 index 000000000..374f80336 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/pkce/PKCECodeChallengeMethod.java @@ -0,0 +1,24 @@ +package com.github.scribejava.core.pkce; + +import com.github.scribejava.core.base64.Base64; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public enum PKCECodeChallengeMethod { + S256 { + @Override + public String transform2CodeChallenge(String codeVerifier) throws NoSuchAlgorithmException { + return Base64.encodeUrlWithoutPadding( + MessageDigest.getInstance("SHA-256").digest(codeVerifier.getBytes(StandardCharsets.US_ASCII))); + } + }, + PLAIN { + @Override + public String transform2CodeChallenge(String codeVerifier) { + return codeVerifier; + } + }; + + public abstract String transform2CodeChallenge(String codeVerifier) throws NoSuchAlgorithmException; +} 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 new file mode 100644 index 000000000..23a5347ec --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/pkce/PKCEService.java @@ -0,0 +1,62 @@ +package com.github.scribejava.core.pkce; + +import com.github.scribejava.core.base64.Base64; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +/** + * Used to implement Proof Key for Code Exchange by OAuth Public Clients https://tools.ietf.org/html/rfc7636 + * + */ +public class PKCEService { + + private static final SecureRandom RANDOM = new SecureRandom(); + /** + * number of octets to randomly generate. + */ + private final int numberOFOctets; + + public PKCEService(int numberOFOctets) { + this.numberOFOctets = numberOFOctets; + } + + /** + * will create random generator with recommended params (32 octets) https://tools.ietf.org/html/rfc7636#section-4.1 + */ + public PKCEService() { + this(32); + } + + private static class DefaultInstanceHolder { + + private static final PKCEService INSTANCE = new PKCEService(); + } + + public static PKCEService defaultInstance() { + return DefaultInstanceHolder.INSTANCE; + } + + public PKCE generatePKCE() { + final byte[] bytes = new byte[numberOFOctets]; + RANDOM.nextBytes(bytes); + return generatePKCE(bytes); + } + + public PKCE generatePKCE(byte[] randomBytes) { + final String codeVerifier = Base64.encodeUrlWithoutPadding(randomBytes); + + final PKCE pkce = new PKCE(); + pkce.setCodeVerifier(codeVerifier); + try { + pkce.setCodeChallenge(pkce.getCodeChallengeMethod().transform2CodeChallenge(codeVerifier)); + } catch (NoSuchAlgorithmException nsaE) { + pkce.setCodeChallengeMethod(PKCECodeChallengeMethod.PLAIN); + try { + pkce.setCodeChallenge(PKCECodeChallengeMethod.PLAIN.transform2CodeChallenge(codeVerifier)); + } catch (NoSuchAlgorithmException unrealE) { + throw new IllegalStateException("It's just cannot be", unrealE); + } + } + return pkce; + } +} 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 new file mode 100644 index 000000000..dc595a410 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/revoke/OAuth2RevokeTokenResponseConverter.java @@ -0,0 +1,15 @@ +package com.github.scribejava.core.revoke; + +import com.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor; +import com.github.scribejava.core.model.Response; +import java.io.IOException; + +public class OAuth2RevokeTokenResponseConverter { + + public Void convert(Response response) throws IOException { + if (response.getCode() != 200) { + OAuth2AccessTokenJsonExtractor.instance().generateError(response); + } + return null; + } +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/revoke/TokenTypeHint.java b/scribejava-core/src/main/java/com/github/scribejava/core/revoke/TokenTypeHint.java new file mode 100644 index 000000000..79de78d26 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/revoke/TokenTypeHint.java @@ -0,0 +1,23 @@ +package com.github.scribejava.core.revoke; + +/** + * + * as stated in RFC 7009
+ * 2.1. Revocation Request + * + * @see RFC 7009, 2.1. Revocation Request + */ +public enum TokenTypeHint { + ACCESS_TOKEN("access_token"), + REFRESH_TOKEN("refresh_token"); + + private final String value; + + TokenTypeHint(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/services/Base64Encoder.java b/scribejava-core/src/main/java/com/github/scribejava/core/services/Base64Encoder.java deleted file mode 100644 index ab564941c..000000000 --- a/scribejava-core/src/main/java/com/github/scribejava/core/services/Base64Encoder.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.github.scribejava.core.services; - -public abstract class Base64Encoder { - - private static Base64Encoder instance; - - public static Base64Encoder getInstance() { - synchronized (Base64Encoder.class) { - if (instance == null) { - instance = createEncoderInstance(); - } - } - return instance; - } - - private static Base64Encoder createEncoderInstance() { - if (CommonsEncoder.isPresent()) { - return new CommonsEncoder(); - } else { - return new DatatypeConverterEncoder(); - } - } - - public static String type() { - return getInstance().getType(); - } - - public abstract String encode(byte[] bytes); - - public abstract String getType(); -} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/services/CommonsEncoder.java b/scribejava-core/src/main/java/com/github/scribejava/core/services/CommonsEncoder.java deleted file mode 100644 index 6e2afa24d..000000000 --- a/scribejava-core/src/main/java/com/github/scribejava/core/services/CommonsEncoder.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.github.scribejava.core.services; - -import java.io.UnsupportedEncodingException; -import org.apache.commons.codec.binary.Base64; -import com.github.scribejava.core.exceptions.OAuthSignatureException; - -public class CommonsEncoder extends Base64Encoder { - - @Override - public String encode(byte[] bytes) { - try { - return new String(Base64.encodeBase64(bytes), "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new OAuthSignatureException("Can't perform base64 encoding", e); - } - } - - @Override - public String getType() { - return "CommonsCodec"; - } - - public static boolean isPresent() { - try { - Class.forName("org.apache.commons.codec.binary.Base64"); - return true; - } catch (ClassNotFoundException e) { - return false; - } - } -} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/services/DatatypeConverterEncoder.java b/scribejava-core/src/main/java/com/github/scribejava/core/services/DatatypeConverterEncoder.java deleted file mode 100644 index eee87714f..000000000 --- a/scribejava-core/src/main/java/com/github/scribejava/core/services/DatatypeConverterEncoder.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.github.scribejava.core.services; - -import javax.xml.bind.DatatypeConverter; - -public class DatatypeConverterEncoder extends Base64Encoder { - - @Override - public String encode(byte[] bytes) { - return DatatypeConverter.printBase64Binary(bytes); - } - - @Override - public String getType() { - return "DatatypeConverter"; - } -} 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 824f488d7..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,7 +11,7 @@ import com.github.scribejava.core.utils.Preconditions; /** - * HMAC-SHA1 implementation of {@link SignatureService} + * HMAC-SHA1 implementation of {@link SignatureService} https://tools.ietf.org/html/rfc5849#section-3.4.2 */ public class HMACSha1SignatureService implements SignatureService { @@ -26,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); @@ -40,11 +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 bytesToBase64String(bytes).replace(CARRIAGE_RETURN, EMPTY_STRING); - } - - private String bytesToBase64String(byte[] bytes) { - return Base64Encoder.getInstance().encode(bytes); + 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 78ecfa2a1..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,17 +33,13 @@ 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 bytesToBase64String(signature); - } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException | UnsupportedEncodingException | - RuntimeException e) { + return Base64.encode(signature.sign()); + } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException | UnsupportedEncodingException + | RuntimeException e) { throw new OAuthSignatureException(baseString, e); } } - private String bytesToBase64String(Signature signature) throws SignatureException { - return Base64Encoder.getInstance().encode(signature.sign()); - } - /** * {@inheritDoc} */ 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 cf821d089..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,7 +1,7 @@ package com.github.scribejava.core.services; /** - * Signs a base string, returning the OAuth signature + * Signs a base string, returning the OAuth signature https://tools.ietf.org/html/rfc5849#section-3.4 */ public interface SignatureService { diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/utils/MapUtils.java b/scribejava-core/src/main/java/com/github/scribejava/core/utils/MapUtils.java deleted file mode 100644 index 9cb217cb9..000000000 --- a/scribejava-core/src/main/java/com/github/scribejava/core/utils/MapUtils.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.github.scribejava.core.utils; - -import java.util.Map; - -public abstract class MapUtils { - - public static String toString(Map map) { - if (map == null) { - return ""; - } - if (map.isEmpty()) { - return "{}"; - } - - final StringBuilder result = new StringBuilder(); - for (Map.Entry entry : map.entrySet()) { - result.append(", ") - .append(entry.getKey().toString()) - .append(" -> ") - .append(entry.getValue().toString()) - .append(' '); - } - return "{" + result.append('}').substring(1); - } -} diff --git a/scribejava-core/src/test/java/com/github/scribejava/core/AbstractClientTest.java b/scribejava-core/src/test/java/com/github/scribejava/core/AbstractClientTest.java new file mode 100644 index 000000000..c1bc1967e --- /dev/null +++ b/scribejava-core/src/test/java/com/github/scribejava/core/AbstractClientTest.java @@ -0,0 +1,264 @@ +package com.github.scribejava.core; + +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.model.OAuthAsyncRequestCallback; +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 com.github.scribejava.core.oauth.OAuthService; +import com.github.scribejava.core.utils.StreamUtils; +import okhttp3.HttpUrl; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertNotNull; + +public abstract class AbstractClientTest { + + private OAuthService oAuthService; + + private static class TestCallback implements OAuthAsyncRequestCallback { + + private Response response; + + @Override + public void onCompleted(Response response) { + this.response = response; + } + + @Override + public void onThrowable(Throwable throwable) { + } + + public Response getResponse() { + return response; + } + } + + @Before + public void setUp() { + oAuthService = new OAuth20Service(null, "test", "test", null, null, null, System.out, null, null, + createNewClient()); + } + + @After + public void shutDown() throws Exception { + oAuthService.close(); + } + + protected abstract HttpClient createNewClient(); + + @Test + public void shouldSendGetRequest() throws Exception { + final String expectedResponseBody = "response body for test shouldSendGetRequest"; + + final MockWebServer server = new MockWebServer(); + server.enqueue(new MockResponse().setBody(expectedResponseBody)); + server.start(); + + final HttpUrl baseUrl = server.url("/testUrl"); + + final OAuthRequest request = new OAuthRequest(Verb.GET, baseUrl.toString()); + + try (Response response = oAuthService.execute(request, null).get(30, TimeUnit.SECONDS)) { + assertEquals(expectedResponseBody, response.getBody()); + } + + final RecordedRequest recordedRequest = server.takeRequest(); + assertEquals("GET", recordedRequest.getMethod()); + + server.shutdown(); + } + + @Test + public void shouldSendPostWithApplicationXWwwFormUrlencodedRequestContentTypeHeader() throws Exception { + final MockWebServer server = new MockWebServer(); + server.enqueue(new MockResponse()); + server.start(); + + final HttpUrl baseUrl = server.url("/testUrl"); + + final OAuthRequest request = new OAuthRequest(Verb.POST, baseUrl.toString()); + oAuthService.execute(request, null).get(30, TimeUnit.SECONDS).close(); + + final RecordedRequest recordedRequest = server.takeRequest(); + assertEquals("POST", recordedRequest.getMethod()); + assertEquals(HttpClient.DEFAULT_CONTENT_TYPE, recordedRequest.getHeader(HttpClient.CONTENT_TYPE)); + + server.shutdown(); + } + + @Test + public void shouldSendPostRequestWithEmptyBody() throws Exception { + final String expectedResponseBody = "response body for test shouldSendPostRequest"; + final String expectedRequestBody = ""; + + final MockWebServer server = new MockWebServer(); + server.enqueue(new MockResponse().setBody(expectedResponseBody)); + server.start(); + + final HttpUrl baseUrl = server.url("/testUrl"); + + final OAuthRequest request = new OAuthRequest(Verb.POST, baseUrl.toString()); + try (Response response = oAuthService.execute(request, null).get(30, TimeUnit.SECONDS)) { + assertEquals(expectedResponseBody, response.getBody()); + } + + final RecordedRequest recordedRequest = server.takeRequest(); + assertEquals("POST", recordedRequest.getMethod()); + assertEquals(expectedRequestBody, recordedRequest.getBody().readUtf8()); + assertEquals(HttpClient.DEFAULT_CONTENT_TYPE, recordedRequest.getHeader(HttpClient.CONTENT_TYPE)); + + server.shutdown(); + } + + @Test + public void shouldSendPostRequestWithStringBody() throws Exception { + final String expectedResponseBody = "response body for test shouldSendPostRequest"; + final String expectedRequestBody = "request body"; + + final MockWebServer server = new MockWebServer(); + server.enqueue(new MockResponse().setBody(expectedResponseBody)); + server.start(); + + final HttpUrl baseUrl = server.url("/testUrl"); + + final OAuthRequest request = new OAuthRequest(Verb.POST, baseUrl.toString()); + request.setPayload(expectedRequestBody); + try (Response response = oAuthService.execute(request, null).get(30, TimeUnit.SECONDS)) { + assertEquals(expectedResponseBody, response.getBody()); + } + + final RecordedRequest recordedRequest = server.takeRequest(); + assertEquals("POST", recordedRequest.getMethod()); + assertEquals(expectedRequestBody, recordedRequest.getBody().readUtf8()); + final String contentTypeHeader = recordedRequest.getHeader(HttpClient.CONTENT_TYPE); + assertNotNull(contentTypeHeader); + assertTrue(contentTypeHeader.startsWith(HttpClient.DEFAULT_CONTENT_TYPE)); + + server.shutdown(); + } + + @Test + public void shouldSendPostRequestWithByteBodyBody() throws Exception { + final String expectedResponseBody = "response body for test shouldSendPostRequest"; + final String expectedRequestBody = "request body"; + + final MockWebServer server = new MockWebServer(); + server.enqueue(new MockResponse().setBody(expectedResponseBody)); + server.start(); + + final HttpUrl baseUrl = server.url("/testUrl"); + + final OAuthRequest request = new OAuthRequest(Verb.POST, baseUrl.toString()); + request.setPayload(expectedRequestBody.getBytes()); + try (Response response = oAuthService.execute(request, null).get(30, TimeUnit.SECONDS)) { + assertEquals(expectedResponseBody, response.getBody()); + } + + final RecordedRequest recordedRequest = server.takeRequest(); + assertEquals("POST", recordedRequest.getMethod()); + assertEquals(expectedRequestBody, recordedRequest.getBody().readUtf8()); + assertEquals(HttpClient.DEFAULT_CONTENT_TYPE, recordedRequest.getHeader(HttpClient.CONTENT_TYPE)); + + server.shutdown(); + } + + @Test + public void shouldSendPostRequestWithBodyParamsBody() throws Exception { + final String expectedResponseBody = "response body for test shouldSendPostRequest"; + final String expectedRequestBodyParamName = "request_body_param_name"; + final String expectedRequestBodyParamValue = "request_body_param_value"; + final String expectedRequestBody = expectedRequestBodyParamName + '=' + expectedRequestBodyParamValue; + + final MockWebServer server = new MockWebServer(); + server.enqueue(new MockResponse().setBody(expectedResponseBody)); + server.start(); + + final HttpUrl baseUrl = server.url("/testUrl"); + + final OAuthRequest request = new OAuthRequest(Verb.POST, baseUrl.toString()); + request.addBodyParameter(expectedRequestBodyParamName, expectedRequestBodyParamValue); + try (Response response = oAuthService.execute(request, null).get(30, TimeUnit.SECONDS)) { + assertEquals(expectedResponseBody, response.getBody()); + } + + final RecordedRequest recordedRequest = server.takeRequest(); + assertEquals("POST", recordedRequest.getMethod()); + assertEquals(expectedRequestBody, recordedRequest.getBody().readUtf8()); + assertEquals(HttpClient.DEFAULT_CONTENT_TYPE, recordedRequest.getHeader(HttpClient.CONTENT_TYPE)); + + server.shutdown(); + } + + @Test + public void shouldReadResponseStream() throws Exception { + final String expectedResponseBody = "response body for test shouldReadResponseStream"; + + final MockWebServer server = new MockWebServer(); + server.enqueue(new MockResponse().setBody(expectedResponseBody)); + server.start(); + + final HttpUrl baseUrl = server.url("/testUrl"); + + final OAuthRequest request = new OAuthRequest(Verb.GET, baseUrl.toString()); + try (Response response = oAuthService.execute(request, null).get(30, TimeUnit.SECONDS)) { + assertEquals(expectedResponseBody, StreamUtils.getStreamContents(response.getStream())); + } + + final RecordedRequest recordedRequest = server.takeRequest(); + assertEquals("GET", recordedRequest.getMethod()); + + server.shutdown(); + } + + @Test + public void shouldCallCallback() throws Exception { + final String expectedResponseBody = "response body for test shouldCallCallback"; + + final MockWebServer server = new MockWebServer(); + server.enqueue(new MockResponse().setBody(expectedResponseBody)); + server.start(); + + final HttpUrl baseUrl = server.url("/testUrl"); + + final OAuthRequest request = new OAuthRequest(Verb.GET, baseUrl.toString()); + + final TestCallback callback = new TestCallback(); + oAuthService.execute(request, callback).get(); + + assertEquals(expectedResponseBody, callback.getResponse().getBody()); + + server.shutdown(); + } + + @Test + public void shouldPassErrors() throws Exception { + + final MockWebServer server = new MockWebServer(); + server.enqueue(new MockResponse().setResponseCode(500)); + server.start(); + + final HttpUrl baseUrl = server.url("/testUrl"); + + final OAuthRequest request = new OAuthRequest(Verb.GET, baseUrl.toString()); + + final TestCallback callback = new TestCallback(); + try (Response response = oAuthService.execute(request, callback).get()) { + + assertEquals(500, response.getCode()); + assertEquals(500, callback.getResponse().getCode()); + } + + server.shutdown(); + } +} 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/builder/ServiceBuilderTest.java b/scribejava-core/src/test/java/com/github/scribejava/core/builder/ServiceBuilderTest.java deleted file mode 100644 index c7d4a4dac..000000000 --- a/scribejava-core/src/test/java/com/github/scribejava/core/builder/ServiceBuilderTest.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.github.scribejava.core.builder; - -import static org.junit.Assert.assertEquals; -import org.junit.Before; -import org.junit.Test; -import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.model.OAuthConfig; -import com.github.scribejava.core.model.OAuthConstants; -import com.github.scribejava.core.oauth.OAuth20Service; - -public class ServiceBuilderTest { - - private ServiceBuilder builder; - private ApiMock api; - - @Before - public void setUp() { - builder = new ServiceBuilder("will override api_key by another later in test"); - api = ApiMock.instance(); - } - - @Test - public void shouldReturnConfigDefaultValues() { - builder.apiKey("key").apiSecret("secret").build(api); - - final OAuthConfig config = api.getConfig(); - assertEquals(config.getApiKey(), "key"); - assertEquals(config.getApiSecret(), "secret"); - assertEquals(config.getCallback(), OAuthConstants.OUT_OF_BAND); - } - - @Test - public void shouldAcceptValidCallbackUrl() { - builder.apiKey("key").apiSecret("secret").callback("http://example.com").build(api); - - final OAuthConfig config = api.getConfig(); - assertEquals(config.getApiKey(), "key"); - assertEquals(config.getApiSecret(), "secret"); - assertEquals(config.getCallback(), "http://example.com"); - } - - @Test(expected = IllegalArgumentException.class) - public void shouldNotAcceptNullAsCallback() { - builder.apiKey("key").apiSecret("secret").callback(null).build(api); - } - - @Test - public void shouldAcceptAnScope() { - builder.apiKey("key").apiSecret("secret").scope("rss-api").build(api); - - final OAuthConfig config = api.getConfig(); - assertEquals(config.getApiKey(), "key"); - assertEquals(config.getApiSecret(), "secret"); - assertEquals(config.getScope(), "rss-api"); - } - - private static class ApiMock extends DefaultApi20 { - - private OAuthConfig config; - - private static ApiMock instance() { - return new ApiMock(); - } - - private OAuthConfig getConfig() { - return config; - } - - @Override - public OAuth20Service createService(OAuthConfig config) { - this.config = config; - return null; - } - - @Override - public String getAccessTokenEndpoint() { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - protected String getAuthorizationBaseUrl() { - throw new UnsupportedOperationException("Not supported yet."); - } - } -} 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 36ac4cd2e..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 { @@ -22,58 +24,94 @@ public void setUp() { @Test public void shouldExtractTokenFromOAuthStandardResponse() throws IOException { - final String response = "oauth_token=hh5s93j4hdidpola&oauth_token_secret=hdhd0244k9j7ao03"; - final OAuth1Token extracted = extractor.extract(ok(response)); + final String responseBody = "oauth_token=hh5s93j4hdidpola&oauth_token_secret=hdhd0244k9j7ao03"; + final OAuth1Token extracted; + try (Response response = ok(responseBody)) { + extracted = extractor.extract(response); + } assertEquals("hh5s93j4hdidpola", extracted.getToken()); assertEquals("hdhd0244k9j7ao03", extracted.getTokenSecret()); } @Test public void shouldExtractTokenFromInvertedOAuthStandardResponse() throws IOException { - final String response = "oauth_token_secret=hh5s93j4hdidpola&oauth_token=hdhd0244k9j7ao03"; - final OAuth1Token extracted = extractor.extract(ok(response)); + final String responseBody = "oauth_token_secret=hh5s93j4hdidpola&oauth_token=hdhd0244k9j7ao03"; + final OAuth1Token extracted; + try (Response response = ok(responseBody)) { + extracted = extractor.extract(response); + } assertEquals("hh5s93j4hdidpola", extracted.getTokenSecret()); assertEquals("hdhd0244k9j7ao03", extracted.getToken()); } @Test public void shouldExtractTokenFromResponseWithCallbackConfirmed() throws IOException { - final String response = "oauth_token=hh5s93j4hdidpola&oauth_token_secret=hdhd0244k9j7ao03" + final String responseBody = "oauth_token=hh5s93j4hdidpola&oauth_token_secret=hdhd0244k9j7ao03" + "&callback_confirmed=true"; - final OAuth1Token extracted = extractor.extract(ok(response)); + final OAuth1Token extracted; + try (Response response = ok(responseBody)) { + extracted = extractor.extract(response); + } assertEquals("hh5s93j4hdidpola", extracted.getToken()); assertEquals("hdhd0244k9j7ao03", extracted.getTokenSecret()); } @Test public void shouldExtractTokenWithEmptySecret() throws IOException { - final String response = "oauth_token=hh5s93j4hdidpola&oauth_token_secret="; - final OAuth1Token extracted = extractor.extract(ok(response)); + final String responseBody = "oauth_token=hh5s93j4hdidpola&oauth_token_secret="; + final OAuth1Token extracted; + try (Response response = ok(responseBody)) { + extracted = extractor.extract(response); + } assertEquals("hh5s93j4hdidpola", extracted.getToken()); assertEquals("", extracted.getTokenSecret()); } - @Test(expected = OAuthException.class) public void shouldThrowExceptionIfTokenIsAbsent() throws IOException { - final String response = "oauth_secret=hh5s93j4hdidpola&callback_confirmed=true"; - extractor.extract(ok(response)); + final String responseBody = "oauth_secret=hh5s93j4hdidpola&callback_confirmed=true"; + try (Response response = ok(responseBody)) { + 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 response = "oauth_token=hh5s93j4hdidpola&callback_confirmed=true"; - extractor.extract(ok(response)); + final String responseBody = "oauth_token=hh5s93j4hdidpola&callback_confirmed=true"; + try (Response response = ok(responseBody)) { + assertThrows(OAuthException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(response); + } + }); + } } - @Test(expected = IllegalArgumentException.class) public void shouldThrowExceptionIfResponseIsNull() throws IOException { - extractor.extract(ok(null)); + try (Response response = ok(null)) { + 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 response = ""; - extractor.extract(ok(response)); + final String responseBody = ""; + try (Response response = ok(responseBody)) { + assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(response); + } + }); + } } private static Response ok(String body) { 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 7f2cd4ddd..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 { @@ -22,18 +24,24 @@ public void setUp() { @Test public void shouldExtractTokenFromOAuthStandardResponse() throws IOException { - final String response = "access_token=166942940015970|2.2ltzWXYNDjCtg5ZDVVJJeg__.3600.1295816400-548517159" + final String responseBody = "access_token=166942940015970|2.2ltzWXYNDjCtg5ZDVVJJeg__.3600.1295816400-548517159" + "|RsXNdKrpxg8L6QNLWcs2TVTmcaE"; - final OAuth2AccessToken extracted = extractor.extract(ok(response)); + final OAuth2AccessToken extracted; + try (Response response = ok(responseBody)) { + extracted = extractor.extract(response); + } assertEquals("166942940015970|2.2ltzWXYNDjCtg5ZDVVJJeg__.3600.1295816400-548517159|RsXNdKrpxg8L6QNLWcs2TVTmcaE", extracted.getAccessToken()); } @Test public void shouldExtractTokenFromResponseWithExpiresParam() throws IOException { - final String response = "access_token=166942940015970|2.2ltzWXYNDjCtg5ZDVVJJeg__.3600.1295816400-548517159" + final String responseBody = "access_token=166942940015970|2.2ltzWXYNDjCtg5ZDVVJJeg__.3600.1295816400-548517159" + "|RsXNdKrpxg8L6QNLWcs2TVTmcaE&expires_in=5108"; - final OAuth2AccessToken extracted = extractor.extract(ok(response)); + final OAuth2AccessToken extracted; + try (Response response = ok(responseBody)) { + extracted = extractor.extract(response); + } assertEquals("166942940015970|2.2ltzWXYNDjCtg5ZDVVJJeg__.3600.1295816400-548517159|RsXNdKrpxg8L6QNLWcs2TVTmcaE", extracted.getAccessToken()); assertEquals(Integer.valueOf(5108), extracted.getExpiresIn()); @@ -41,9 +49,12 @@ public void shouldExtractTokenFromResponseWithExpiresParam() throws IOException @Test public void shouldExtractTokenFromResponseWithExpiresAndRefreshParam() throws IOException { - final String response = "access_token=166942940015970|2.2ltzWXYNDjCtg5ZDVVJJeg__.3600.1295816400-548517159" + final String responseBody = "access_token=166942940015970|2.2ltzWXYNDjCtg5ZDVVJJeg__.3600.1295816400-548517159" + "|RsXNdKrpxg8L6QNLWcs2TVTmcaE&expires_in=5108&token_type=bearer&refresh_token=166942940015970"; - final OAuth2AccessToken extracted = extractor.extract(ok(response)); + final OAuth2AccessToken extracted; + try (Response response = ok(responseBody)) { + extracted = extractor.extract(response); + } assertEquals("166942940015970|2.2ltzWXYNDjCtg5ZDVVJJeg__.3600.1295816400-548517159|RsXNdKrpxg8L6QNLWcs2TVTmcaE", extracted.getAccessToken()); assertEquals(Integer.valueOf(5108), extracted.getExpiresIn()); @@ -53,29 +64,66 @@ public void shouldExtractTokenFromResponseWithExpiresAndRefreshParam() throws IO @Test public void shouldExtractTokenFromResponseWithManyParameters() throws IOException { - final String response = "access_token=foo1234&other_stuff=yeah_we_have_this_too&number=42"; - final OAuth2AccessToken extracted = extractor.extract(ok(response)); + final String responseBody = "access_token=foo1234&other_stuff=yeah_we_have_this_too&number=42"; + final OAuth2AccessToken extracted; + try (Response response = ok(responseBody)) { + extracted = extractor.extract(response); + } assertEquals("foo1234", extracted.getAccessToken()); } - @Test(expected = OAuthException.class) + public void shouldThrowExceptionIfErrorResponse() throws IOException { + final String responseBody = ""; + try (Response response = error(responseBody)) { + assertThrows(OAuthException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(response); + } + }); + } + } + public void shouldThrowExceptionIfTokenIsAbsent() throws IOException { - final String response = "&expires=5108"; - extractor.extract(ok(response)); + final String responseBody = "&expires=5108"; + try (Response response = ok(responseBody)) { + assertThrows(OAuthException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(response); + } + }); + } } - @Test(expected = IllegalArgumentException.class) public void shouldThrowExceptionIfResponseIsNull() throws IOException { - extractor.extract(ok(null)); + try (Response response = ok(null)) { + 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 response = ""; - extractor.extract(ok(response)); + final String responseBody = ""; + try (Response response = ok(responseBody)) { + assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(response); + } + }); + } } private static Response ok(String body) { return new Response(200, /* message */ null, /* headers */ Collections.emptyMap(), body); } + + private static Response error(String body) { + return new Response(400, /* message */ null, /* headers */ Collections.emptyMap(), body); + } } 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 42f07e25f..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 @@ -3,50 +3,119 @@ import com.github.scribejava.core.model.OAuth2AccessToken; import com.github.scribejava.core.model.OAuth2AccessTokenErrorResponse; import com.github.scribejava.core.model.Response; +import com.github.scribejava.core.oauth2.OAuth2Error; import org.junit.Test; 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.function.ThrowingRunnable; public class OAuth2AccessTokenJsonExtractorTest { - private static final String RESPONSE = "'{ \"access_token\":\"I0122HHJKLEM21F3WLPYHDKGKZULAUO4SGMV3ABKFTDT3T3X\"}'"; private final OAuth2AccessTokenJsonExtractor extractor = OAuth2AccessTokenJsonExtractor.instance(); @Test public void shouldParseResponse() throws IOException { - final OAuth2AccessToken token = extractor.extract(ok(RESPONSE)); - assertEquals(token.getAccessToken(), "I0122HHJKLEM21F3WLPYHDKGKZULAUO4SGMV3ABKFTDT3T3X"); + final String responseBody = "{ \"access_token\":\"I0122HHJKLEM21F3WLPYHDKGKZULAUO4SGMV3ABKFTDT3T3X\", " + + "\"token_type\":\"example\"}"; + final OAuth2AccessToken token; + try (Response response = ok(responseBody)) { + token = extractor.extract(response); + } + assertEquals("I0122HHJKLEM21F3WLPYHDKGKZULAUO4SGMV3ABKFTDT3T3X", token.getAccessToken()); + } + + @Test + public void shouldParseScopeFromResponse() throws IOException { + final String responseBody = "{ \"access_token\":\"I0122HHJKLEM21F3WLPYHDKGKZULAUO4SGMV3ABKFTDT3T4X\", " + + "\"token_type\":\"example\"," + + "\"scope\":\"s1\"}"; + final OAuth2AccessToken token; + try (Response response = ok(responseBody)) { + token = extractor.extract(response); + } + assertEquals("I0122HHJKLEM21F3WLPYHDKGKZULAUO4SGMV3ABKFTDT3T4X", token.getAccessToken()); + assertEquals("s1", token.getScope()); + + final String responseBody2 = "{ \"access_token\":\"I0122HHJKLEM21F3WLPYHDKGKZULAUO4SGMV3ABKFTDT3T5X\", " + + "\"token_type\":\"example\"," + + "\"scope\":\"s1 s2\"}"; + final OAuth2AccessToken token2; + try (Response response = ok(responseBody2)) { + token2 = extractor.extract(response); + } + assertEquals("I0122HHJKLEM21F3WLPYHDKGKZULAUO4SGMV3ABKFTDT3T5X", token2.getAccessToken()); + assertEquals("s1 s2", token2.getScope()); + + final String responseBody3 = "{ \"access_token\":\"I0122HHJKLEM21F3WLPYHDKGKZULAUO4SGMV3ABKFTDT3T6X\", " + + "\"token_type\":\"example\"," + + "\"scope\":\"s3 s4\", " + + "\"refresh_token\":\"refresh_token1\"}"; + final OAuth2AccessToken token3; + try (Response response = ok(responseBody3)) { + token3 = extractor.extract(response); + } + assertEquals("I0122HHJKLEM21F3WLPYHDKGKZULAUO4SGMV3ABKFTDT3T6X", token3.getAccessToken()); + assertEquals("s3 s4", token3.getScope()); + assertEquals("refresh_token1", token3.getRefreshToken()); } - @Test(expected = IllegalArgumentException.class) public void shouldThrowExceptionIfForNullParameters() throws IOException { - extractor.extract(ok(null)); + try (Response response = ok(null)) { + assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(response); + } + }); + } } - @Test(expected = IllegalArgumentException.class) public void shouldThrowExceptionIfForEmptyStrings() throws IOException { - extractor.extract(ok("")); + final String responseBody = ""; + try (Response response = ok(responseBody)) { + assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(response); + } + }); + } } @Test public void shouldThrowExceptionIfResponseIsError() throws IOException { - final String body = "{" + - "\"error_description\":\"unknown, invalid, or expired refresh token\"," + - "\"error\":\"invalid_grant\"" + - "}"; - try { - extractor.extract(error(body)); - fail(); - } catch (OAuth2AccessTokenErrorResponse oaer) { - assertEquals(OAuth2AccessTokenErrorResponse.ErrorCode.invalid_grant, oaer.getErrorCode()); + final String responseBody = "{" + + "\"error_description\":\"unknown, invalid, or expired refresh token\"," + + "\"error\":\"invalid_grant\"" + + "}"; + try (Response response = error(responseBody)) { + 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()); } } + @Test + public void testEscapedJsonInResponse() throws IOException { + final String responseBody = "{ \"access_token\":\"I0122HKLEM2\\/MV3ABKFTDT3T5X\"," + + "\"token_type\":\"example\"}"; + final OAuth2AccessToken token; + try (Response response = ok(responseBody)) { + token = extractor.extract(response); + } + assertEquals("I0122HKLEM2/MV3ABKFTDT3T5X", token.getAccessToken()); + } + private static Response ok(String body) { return new Response(200, /* message */ null, /* headers */ Collections.emptyMap(), body); } diff --git a/scribejava-core/src/test/java/com/github/scribejava/core/httpclient/jdk/JDKHttpClientTest.java b/scribejava-core/src/test/java/com/github/scribejava/core/httpclient/jdk/JDKHttpClientTest.java new file mode 100644 index 000000000..6ac1070fa --- /dev/null +++ b/scribejava-core/src/test/java/com/github/scribejava/core/httpclient/jdk/JDKHttpClientTest.java @@ -0,0 +1,12 @@ +package com.github.scribejava.core.httpclient.jdk; + +import com.github.scribejava.core.AbstractClientTest; +import com.github.scribejava.core.httpclient.HttpClient; + +public class JDKHttpClientTest extends AbstractClientTest { + + @Override + protected HttpClient createNewClient() { + return new JDKHttpClient(); + } +} diff --git a/scribejava-core/src/test/java/com/github/scribejava/core/httpclient/multipart/MultipartUtilsTest.java b/scribejava-core/src/test/java/com/github/scribejava/core/httpclient/multipart/MultipartUtilsTest.java new file mode 100644 index 000000000..c726dd9a7 --- /dev/null +++ b/scribejava-core/src/test/java/com/github/scribejava/core/httpclient/multipart/MultipartUtilsTest.java @@ -0,0 +1,353 @@ +package com.github.scribejava.core.httpclient.multipart; + +import com.github.scribejava.core.httpclient.HttpClient; +import java.io.IOException; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import org.junit.Test; +import org.junit.function.ThrowingRunnable; + +public class MultipartUtilsTest { + + @Test + public void testEmptyMultipartPayload() throws IOException { + final MultipartPayload mP = new MultipartPayload(); + + final StringBuilder headersString = new StringBuilder(); + for (Map.Entry header : mP.getHeaders().entrySet()) { + headersString.append(header.getKey()) + .append(": ") + .append(header.getValue()) + .append("\r\n"); + } + + assertEquals("Content-Type: multipart/form-data; boundary=\"" + mP.getBoundary() + "\"\r\n", + headersString.toString()); + + assertEquals("", MultipartUtils.getPayload(mP).toString()); + } + + @Test + public void testSimpleMultipartPayload() throws IOException { + final Map headers = new LinkedHashMap<>(); + headers.put("X-Header", "X-Value"); + headers.put("Content-Disposition", "Content-Disposition-Value"); + final MultipartPayload mP = new MultipartPayload("mixed", "simple boundary", headers); + mP.setPreamble("This is the preamble. It is to be ignored, though it\n" + + "is a handy place for composition agents to include an\n" + + "explanatory note to non-MIME conformant readers."); + + mP.addBodyPart(new ByteArrayBodyPartPayload(("This is implicitly typed plain US-ASCII text.\n" + + "It does NOT end with a linebreak.").getBytes())); + + final ByteArrayBodyPartPayload bP = new ByteArrayBodyPartPayload( + ("This is explicitly typed plain US-ASCII text.\n" + + "It DOES end with a linebreak.\n").getBytes(), + Collections.singletonMap(HttpClient.CONTENT_TYPE, "text/plain; charset=us-ascii")); + mP.addBodyPart(bP); + + mP.setEpilogue("This is the epilogue. It is also to be ignored."); + + final StringBuilder headersString = new StringBuilder(); + for (Map.Entry header : mP.getHeaders().entrySet()) { + headersString.append(header.getKey()) + .append(": ") + .append(header.getValue()) + .append("\r\n"); + } + + assertEquals("X-Header: X-Value\r\n" + + "Content-Disposition: Content-Disposition-Value\r\n" + + "Content-Type: multipart/mixed; boundary=\"simple boundary\"\r\n", + headersString.toString()); + + assertEquals("This is the preamble. It is to be ignored, though it\n" + + "is a handy place for composition agents to include an\n" + + "explanatory note to non-MIME conformant readers." + + "\r\n--simple boundary\r\n" + + "\r\n" + + "This is implicitly typed plain US-ASCII text.\n" + + "It does NOT end with a linebreak." + + "\r\n--simple boundary\r\n" + + "Content-Type: text/plain; charset=us-ascii\r\n" + + "\r\n" + + "This is explicitly typed plain US-ASCII text.\n" + + "It DOES end with a linebreak.\n" + + "\r\n--simple boundary--" + + "\r\nThis is the epilogue. It is also to be ignored.", + MultipartUtils.getPayload(mP).toString()); + } + + @Test + public void testCRLFMultipartPayload() throws IOException { + final MultipartPayload mP = new MultipartPayload("simple-boundary"); + mP.addBodyPart(new ByteArrayBodyPartPayload("It does NOT end with a linebreak.".getBytes())); + mP.addBodyPart(new ByteArrayBodyPartPayload("It does end with a \\r linebreak.\r".getBytes())); + mP.addBodyPart(new ByteArrayBodyPartPayload("It does end with a \\n linebreak.\n".getBytes())); + mP.addBodyPart(new ByteArrayBodyPartPayload("It does end with a \\r\\n linebreak.\r\n".getBytes())); + mP.addBodyPart(new ByteArrayBodyPartPayload("the last one".getBytes())); + + final StringBuilder headersString = new StringBuilder(); + for (Map.Entry header : mP.getHeaders().entrySet()) { + headersString.append(header.getKey()) + .append(": ") + .append(header.getValue()) + .append("\r\n"); + } + + assertEquals("Content-Type: multipart/form-data; boundary=\"simple-boundary\"\r\n", headersString.toString()); + + assertEquals("--simple-boundary\r\n" + + "\r\n" + + "It does NOT end with a linebreak." + + "\r\n--simple-boundary\r\n" + + "\r\n" + + "It does end with a \\r linebreak.\r" + + "\r\n--simple-boundary\r\n" + + "\r\n" + + "It does end with a \\n linebreak.\n" + + "\r\n--simple-boundary\r\n" + + "\r\n" + + "It does end with a \\r\\n linebreak.\r\n" + + "\r\n--simple-boundary\r\n" + + "\r\n" + + "the last one" + + "\r\n--simple-boundary--", + MultipartUtils.getPayload(mP).toString()); + } + + @Test + public void testFileByteArrayBodyPartPayloadMultipartPayload() throws IOException { + final MultipartPayload mP = new MultipartPayload("testFileByteArrayBodyPartPayloadMultipartPayload boundary"); + mP.addBodyPart(new FileByteArrayBodyPartPayload("fileContent".getBytes(), "name", "filename.ext")); + + final StringBuilder headersString = new StringBuilder(); + for (Map.Entry header : mP.getHeaders().entrySet()) { + headersString.append(header.getKey()) + .append(": ") + .append(header.getValue()) + .append("\r\n"); + } + + assertEquals("Content-Type: multipart/form-data; " + + "boundary=\"testFileByteArrayBodyPartPayloadMultipartPayload boundary\"\r\n", + headersString.toString()); + + assertEquals("--testFileByteArrayBodyPartPayloadMultipartPayload boundary\r\n" + + "Content-Disposition: form-data; name=\"name\"; filename=\"filename.ext\"\r\n" + + "\r\n" + + "fileContent" + + "\r\n--testFileByteArrayBodyPartPayloadMultipartPayload boundary--", + MultipartUtils.getPayload(mP).toString()); + } + + @Test + public void testComplexMultipartPayload() throws IOException { + final MultipartPayload mP = new MultipartPayload("mixed", "unique-boundary-1"); + + mP.setPreamble("This is the preamble area of a multipart message.\n" + + "Mail readers that understand multipart format\n" + + "should ignore this preamble.\n" + + "\n" + + "If you are reading this text, you might want to\n" + + "consider changing to a mail reader that understands\n" + + "how to properly display multipart messages.\n"); + + mP.addBodyPart(new ByteArrayBodyPartPayload("... Some text appears here ...".getBytes())); + + mP.addBodyPart(new ByteArrayBodyPartPayload(("This could have been part of the previous part, but\n" + + "illustrates explicit versus implicit typing of body\n" + + "parts.\n").getBytes(), "text/plain; charset=US-ASCII")); + + final MultipartPayload innerMP = new MultipartPayload("parallel", "unique-boundary-2"); + mP.addBodyPart(innerMP); + + final Map audioHeaders = new LinkedHashMap<>(); + audioHeaders.put("Content-Type", "audio/basic"); + audioHeaders.put("Content-Transfer-Encoding", "base64"); + innerMP.addBodyPart(new ByteArrayBodyPartPayload(("... base64-encoded 8000 Hz single-channel\n" + + " mu-law-format audio data goes here ...").getBytes(), audioHeaders)); + + final Map imageHeaders = new LinkedHashMap<>(); + imageHeaders.put("Content-Type", "image/jpeg"); + imageHeaders.put("Content-Transfer-Encoding", "base64"); + innerMP.addBodyPart(new ByteArrayBodyPartPayload("... base64-encoded image data goes here ...".getBytes(), + imageHeaders)); + + mP.addBodyPart(new ByteArrayBodyPartPayload(("This is enriched.\n" + + "as defined in RFC 1896\n" + + "\n" + + "Isn't it\n" + + "cool?\n").getBytes(), "text/enriched")); + + mP.addBodyPart(new ByteArrayBodyPartPayload(("From: (mailbox in US-ASCII)\n" + + "To: (address in US-ASCII)\n" + + "Subject: (subject in US-ASCII)\n" + + "Content-Type: Text/plain; charset=ISO-8859-1\n" + + "Content-Transfer-Encoding: Quoted-printable\n" + + "\n" + + "... Additional text in ISO-8859-1 goes here ...\n").getBytes(), "message/rfc822")); + + final StringBuilder headersString = new StringBuilder(); + for (Map.Entry header : mP.getHeaders().entrySet()) { + headersString.append(header.getKey()) + .append(": ") + .append(header.getValue()) + .append("\r\n"); + } + assertEquals("Content-Type: multipart/mixed; boundary=\"unique-boundary-1\"\r\n", headersString.toString()); + + assertEquals("This is the preamble area of a multipart message.\n" + + "Mail readers that understand multipart format\n" + + "should ignore this preamble.\n" + + "\n" + + "If you are reading this text, you might want to\n" + + "consider changing to a mail reader that understands\n" + + "how to properly display multipart messages.\n" + + "\r\n--unique-boundary-1\r\n" + + "\r\n" + + "... Some text appears here ..." + + "\r\n--unique-boundary-1\r\n" + + "Content-Type: text/plain; charset=US-ASCII\r\n" + + "\r\n" + + "This could have been part of the previous part, but\n" + + "illustrates explicit versus implicit typing of body\n" + + "parts.\n" + + "\r\n--unique-boundary-1\r\n" + + "Content-Type: multipart/parallel; boundary=\"unique-boundary-2\"\r\n" + + "\r\n--unique-boundary-2\r\n" + + "Content-Type: audio/basic\r\n" + + "Content-Transfer-Encoding: base64\r\n" + + "\r\n" + + "... base64-encoded 8000 Hz single-channel\n" + + " mu-law-format audio data goes here ..." + + "\r\n--unique-boundary-2\r\n" + + "Content-Type: image/jpeg\r\n" + + "Content-Transfer-Encoding: base64\r\n" + + "\r\n" + + "... base64-encoded image data goes here ..." + + "\r\n--unique-boundary-2--" + + "\r\n--unique-boundary-1\r\n" + + "Content-Type: text/enriched\r\n" + + "\r\n" + + "This is enriched.\n" + + "as defined in RFC 1896\n" + + "\n" + + "Isn't it\n" + + "cool?\n" + + "\r\n--unique-boundary-1\r\n" + + "Content-Type: message/rfc822\r\n" + + "\r\n" + + "From: (mailbox in US-ASCII)\n" + + "To: (address in US-ASCII)\n" + + "Subject: (subject in US-ASCII)\n" + + "Content-Type: Text/plain; charset=ISO-8859-1\n" + + "Content-Transfer-Encoding: Quoted-printable\n" + + "\n" + + "... Additional text in ISO-8859-1 goes here ...\n" + + "\r\n--unique-boundary-1--", + MultipartUtils.getPayload(mP).toString()); + } + + @Test + public void testParseBoundaryFromHeader() { + assertNull(MultipartUtils.parseBoundaryFromHeader(null)); + + assertEquals("0aA'()+_,-./:=?", + MultipartUtils.parseBoundaryFromHeader("multipart/subtype; boundary=\"0aA'()+_,-./:=?\"")); + + assertEquals("0aA'()+_, -./:=?", + MultipartUtils.parseBoundaryFromHeader("multipart/subtype; boundary=\"0aA'()+_, -./:=?\"")); + + assertEquals("0aA'()+_, -./:=?", + MultipartUtils.parseBoundaryFromHeader("multipart/subtype; boundary=\"0aA'()+_, -./:=? \"")); + + assertEquals("0aA'()+_,-./:=?", + MultipartUtils.parseBoundaryFromHeader("multipart/subtype; boundary=0aA'()+_,-./:=?")); + + assertEquals("0aA'()+_, -./:=?", + MultipartUtils.parseBoundaryFromHeader("multipart/subtype; boundary=0aA'()+_, -./:=?")); + + assertEquals("0aA'()+_, -./:=?", + MultipartUtils.parseBoundaryFromHeader("multipart/subtype; boundary=0aA'()+_, -./:=? ")); + + assertEquals(" 0aA'()+_, -./:=?", + MultipartUtils.parseBoundaryFromHeader("multipart/subtype; boundary= 0aA'()+_, -./:=?")); + + assertNull(MultipartUtils.parseBoundaryFromHeader("multipart/subtype; boundar=0aA'()+_, -./:=? ")); + assertNull(MultipartUtils.parseBoundaryFromHeader("multipart/subtype; ")); + assertNull(MultipartUtils.parseBoundaryFromHeader("multipart/subtype;")); + assertNull(MultipartUtils.parseBoundaryFromHeader("multipart/subtype")); + assertNull(MultipartUtils.parseBoundaryFromHeader("multipart/subtype; boundary=")); + + assertEquals("0aA'()+_,", + MultipartUtils.parseBoundaryFromHeader("multipart/subtype; boundary=0aA'()+_,; -./:=? ")); + + assertEquals("0aA'()+_, -./:=?", + MultipartUtils.parseBoundaryFromHeader("multipart/subtype; boundary=\"0aA'()+_, -./:=?")); + + assertEquals("0aA'()+_, -./:=?", + MultipartUtils.parseBoundaryFromHeader("multipart/subtype; boundary=0aA'()+_, -./:=?\"")); + + assertEquals("1234567890123456789012345678901234567890123456789012345678901234567890", + MultipartUtils.parseBoundaryFromHeader("multipart/subtype; " + + "boundary=1234567890123456789012345678901234567890123456789012345678901234567890")); + + assertEquals("1234567890123456789012345678901234567890123456789012345678901234567890", + MultipartUtils.parseBoundaryFromHeader("multipart/subtype; " + + "boundary=12345678901234567890123456789012345678901234567890123456789012345678901")); + + assertNull(MultipartUtils.parseBoundaryFromHeader("multipart/subtype; boundary=")); + assertNull(MultipartUtils.parseBoundaryFromHeader("multipart/subtype; boundary=\"\"")); + assertNull(MultipartUtils.parseBoundaryFromHeader("multipart/subtype; boundary=;123")); + assertNull(MultipartUtils.parseBoundaryFromHeader("multipart/subtype; boundary=\"\"123")); + } + + @Test + public void testValidCheckBoundarySyntax() { + MultipartUtils.checkBoundarySyntax("0aA'()+_,-./:=?"); + MultipartUtils.checkBoundarySyntax("0aA'()+_,- ./:=?"); + MultipartUtils.checkBoundarySyntax(" 0aA'()+_,-./:=?"); + MultipartUtils.checkBoundarySyntax("1234567890123456789012345678901234567890123456789012345678901234567890"); + } + + @Test + public void testNonValidLastWhiteSpaceCheckBoundarySyntax() { + testNotValidBoundary("0aA'()+_,-./:=? "); + } + + @Test + public void testNonValidEmptyCheckBoundarySyntax() { + testNotValidBoundary(""); + } + + @Test + public void testNonValidIllegalSymbolCheckBoundarySyntax() { + testNotValidBoundary("0aA'()+_;,-./:=? "); + } + + @Test + public void testNonValidTooLongCheckBoundarySyntax() { + testNotValidBoundary("12345678901234567890123456789012345678901234567890123456789012345678901"); + } + + @Test + public void testNonValidNullCheckBoundarySyntax() { + testNotValidBoundary(null); + } + + private static void testNotValidBoundary(final String boundary) { + final IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + MultipartUtils.checkBoundarySyntax(boundary); + } + }); + assertTrue(thrown.getMessage().startsWith("{'boundary'='" + boundary + "'} has invalid syntax. Should be '")); + } +} 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 0ea88985d..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,8 +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 { @@ -24,8 +27,30 @@ 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 + public void shouldNotSentHeaderTwice() { + assertTrue(request.getHeaders().isEmpty()); + request.addHeader("HEADER-NAME", "first"); + request.addHeader("header-name", "middle"); + request.addHeader("Header-Name", "last"); + + assertEquals(1, request.getHeaders().size()); + + assertTrue(request.getHeaders().containsKey("HEADER-NAME")); + assertTrue(request.getHeaders().containsKey("header-name")); + assertTrue(request.getHeaders().containsKey("Header-Name")); + + assertEquals("last", request.getHeaders().get("HEADER-NAME")); + assertEquals("last", request.getHeaders().get("header-name")); + assertEquals("last", request.getHeaders().get("Header-Name")); } } 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/model/RequestTest.java b/scribejava-core/src/test/java/com/github/scribejava/core/model/RequestTest.java index 274ccca23..87189632b 100644 --- a/scribejava-core/src/test/java/com/github/scribejava/core/model/RequestTest.java +++ b/scribejava-core/src/test/java/com/github/scribejava/core/model/RequestTest.java @@ -1,27 +1,21 @@ package com.github.scribejava.core.model; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import org.junit.Before; import org.junit.Test; -import java.net.MalformedURLException; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertArrayEquals; public class RequestTest { - private OAuthRequest getRequest; - private OAuthRequest postRequest; - - @Before - public void setUp() throws MalformedURLException { - postRequest = new OAuthRequest(Verb.POST, "http://example.com"); + @Test + public void shouldGetQueryStringParameters() { + final OAuthRequest postRequest = new OAuthRequest(Verb.POST, "http://example.com"); postRequest.addBodyParameter("param", "value"); postRequest.addBodyParameter("param with spaces", "value with spaces"); - getRequest = new OAuthRequest(Verb.GET, "http://example.com?qsparam=value&other+param=value+with+spaces"); - } + final OAuthRequest getRequest + = new OAuthRequest(Verb.GET, "http://example.com?qsparam=value&other+param=value+with+spaces"); - @Test - public void shouldGetQueryStringParameters() { assertEquals(2, getRequest.getQueryStringParams().size()); assertEquals(0, postRequest.getQueryStringParams().size()); assertTrue(getRequest.getQueryStringParams().contains(new Parameter("qsparam", "value"))); @@ -29,13 +23,21 @@ public void shouldGetQueryStringParameters() { @Test public void shouldSetBodyParamsAndAddContentLength() { + final OAuthRequest postRequest = new OAuthRequest(Verb.POST, "http://example.com"); + postRequest.addBodyParameter("param", "value"); + postRequest.addBodyParameter("param with spaces", "value with spaces"); + assertEquals("param=value¶m%20with%20spaces=value%20with%20spaces", new String(postRequest.getByteArrayPayload())); } @Test public void shouldSetPayloadAndHeaders() { + final OAuthRequest postRequest = new OAuthRequest(Verb.POST, "http://example.com"); + postRequest.addBodyParameter("param", "value"); + postRequest.addBodyParameter("param with spaces", "value with spaces"); postRequest.setPayload("PAYLOAD"); + assertEquals("PAYLOAD", postRequest.getStringPayload()); } @@ -57,6 +59,41 @@ public void shouldReturnTheCompleteUrl() { @Test public void shouldHandleQueryStringSpaceEncodingProperly() { + final OAuthRequest getRequest + = new OAuthRequest(Verb.GET, "http://example.com?qsparam=value&other+param=value+with+spaces"); + assertTrue(getRequest.getQueryStringParams().contains(new Parameter("other param", "value with spaces"))); } + + @Test + public void shouldNotEncodeInStringPayload() throws Exception { + final String requestBody = "~/!@#$%^&*( )_+//\r\n%2F&"; + + final OAuthRequest postRequest = new OAuthRequest(Verb.POST, "http://example.com"); + postRequest.setPayload(requestBody); + + assertEquals(requestBody, postRequest.getStringPayload()); + } + + @Test + public void shouldNotEncodeInByteBodyPayload() throws Exception { + final byte[] requestBody = "~/!@#$%^&*( )_+//\r\n%2F&".getBytes(); + + final OAuthRequest postRequest = new OAuthRequest(Verb.POST, "http://example.com"); + postRequest.setPayload(requestBody); + + assertArrayEquals(requestBody, postRequest.getByteArrayPayload()); + } + + @Test + public void shouldEncodeInBodyParamsPayload() throws Exception { + final String expectedRequestBodyParamName = "~/!@#$%^&*( )_+//\r\n%2F&name"; + final String expectedRequestBodyParamValue = "~/!@#$%^&*( )_+//\r\n%2F&value"; + final String expectedRequestBody = "~%2F%21%40%23%24%25%5E%26%2A%28%20%29_%2B%2F%2F%0D%0A%252F%26amp%3Bname=" + + "~%2F%21%40%23%24%25%5E%26%2A%28%20%29_%2B%2F%2F%0D%0A%252F%26amp%3Bvalue"; + + final OAuthRequest postRequest = new OAuthRequest(Verb.POST, "http://example.com"); + postRequest.addBodyParameter(expectedRequestBodyParamName, expectedRequestBodyParamValue); + assertArrayEquals(expectedRequestBody.getBytes(), postRequest.getByteArrayPayload()); + } } diff --git a/scribejava-core/src/test/java/com/github/scribejava/core/oauth/OAuth20ApiUnit.java b/scribejava-core/src/test/java/com/github/scribejava/core/oauth/OAuth20ApiUnit.java index c6c05e05c..cd9e58550 100644 --- a/scribejava-core/src/test/java/com/github/scribejava/core/oauth/OAuth20ApiUnit.java +++ b/scribejava-core/src/test/java/com/github/scribejava/core/oauth/OAuth20ApiUnit.java @@ -1,8 +1,12 @@ package com.github.scribejava.core.oauth; +import java.io.OutputStream; + import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.builder.api.OAuth2SignatureType; -import com.github.scribejava.core.model.OAuthConfig; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignature; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignatureURIQueryParameter; class OAuth20ApiUnit extends DefaultApi20 { @@ -17,12 +21,15 @@ protected String getAuthorizationBaseUrl() { } @Override - public OAuth20Service createService(OAuthConfig config) { - return new OAuth20ServiceUnit(this, config); + public OAuth20ServiceUnit createService(String apiKey, String apiSecret, String callback, String defaultScope, + String responseType, OutputStream debugStream, String userAgent, HttpClientConfig httpClientConfig, + HttpClient httpClient) { + return new OAuth20ServiceUnit(this, apiKey, apiSecret, callback, defaultScope, responseType, debugStream, + userAgent, httpClientConfig, httpClient); } @Override - public OAuth2SignatureType getSignatureType() { - return OAuth2SignatureType.BEARER_URI_QUERY_PARAMETER; + public BearerSignature getBearerSignature() { + return BearerSignatureURIQueryParameter.instance(); } } 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 581fc5a63..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 @@ -1,23 +1,24 @@ package com.github.scribejava.core.oauth; +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.model.OAuth2AccessToken; import com.github.scribejava.core.model.OAuth2Authorization; import com.github.scribejava.core.model.OAuthConstants; -import com.github.scribejava.core.services.Base64Encoder; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; import java.io.IOException; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertEquals; import org.junit.Test; import java.nio.charset.Charset; -import java.util.Map; import java.util.concurrent.ExecutionException; public class OAuth20ServiceTest { + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + @Test public void shouldProduceCorrectRequestSync() throws IOException, InterruptedException, ExecutionException { final OAuth20Service service = new ServiceBuilder("your_api_key") @@ -25,53 +26,46 @@ public void shouldProduceCorrectRequestSync() throws IOException, InterruptedExc .build(new OAuth20ApiUnit()); final OAuth2AccessToken token = service.getAccessTokenPasswordGrant("user1", "password1"); - final Gson json = new Gson(); - assertNotNull(token); - final Map map = json.fromJson(token.getRawResponse(), new TypeTokenImpl().getType()); + final JsonNode response = OBJECT_MAPPER.readTree(token.getRawResponse()); - assertEquals(OAuth20ServiceUnit.TOKEN, map.get(OAuthConstants.ACCESS_TOKEN)); - assertEquals(OAuth20ServiceUnit.STATE, map.get(OAuthConstants.STATE)); - assertEquals(OAuth20ServiceUnit.EXPIRES, map.get("expires_in")); + assertEquals(OAuth20ServiceUnit.TOKEN, response.get(OAuthConstants.ACCESS_TOKEN).asText()); + assertEquals(OAuth20ServiceUnit.EXPIRES, response.get("expires_in").asInt()); - final String authorize = Base64Encoder.getInstance() - .encode(String.format("%s:%s", service.getConfig().getApiKey(), service.getConfig().getApiSecret()) - .getBytes(Charset.forName("UTF-8"))); + final String authorize = Base64.encode( + String.format("%s:%s", service.getApiKey(), service.getApiSecret()).getBytes(Charset.forName("UTF-8"))); - assertEquals(OAuthConstants.BASIC + " " + authorize, map.get(OAuthConstants.HEADER)); + assertEquals(OAuthConstants.BASIC + ' ' + authorize, response.get(OAuthConstants.HEADER).asText()); - assertEquals("user1", map.get("query-username")); - assertEquals("password1", map.get("query-password")); - assertEquals("password", map.get("query-grant_type")); + assertEquals("user1", response.get("query-username").asText()); + assertEquals("password1", response.get("query-password").asText()); + assertEquals("password", response.get("query-grant_type").asText()); } @Test - public void shouldProduceCorrectRequestAsync() throws ExecutionException, InterruptedException { + public void shouldProduceCorrectRequestAsync() throws ExecutionException, InterruptedException, IOException { final OAuth20Service service = new ServiceBuilder("your_api_key") .apiSecret("your_api_secret") .build(new OAuth20ApiUnit()); - final OAuth2AccessToken token = service.getAccessTokenPasswordGrantAsync("user1", "password1", null).get(); - final Gson json = new Gson(); + final OAuth2AccessToken token = service.getAccessTokenPasswordGrantAsync("user1", "password1").get(); assertNotNull(token); - final Map map = json.fromJson(token.getRawResponse(), new TypeTokenImpl().getType()); + final JsonNode response = OBJECT_MAPPER.readTree(token.getRawResponse()); - assertEquals(OAuth20ServiceUnit.TOKEN, map.get(OAuthConstants.ACCESS_TOKEN)); - assertEquals(OAuth20ServiceUnit.STATE, map.get(OAuthConstants.STATE)); - assertEquals(OAuth20ServiceUnit.EXPIRES, map.get("expires_in")); + assertEquals(OAuth20ServiceUnit.TOKEN, response.get(OAuthConstants.ACCESS_TOKEN).asText()); + assertEquals(OAuth20ServiceUnit.EXPIRES, response.get("expires_in").asInt()); - final String authorize = Base64Encoder.getInstance() - .encode(String.format("%s:%s", service.getConfig().getApiKey(), service.getConfig().getApiSecret()) - .getBytes(Charset.forName("UTF-8"))); + final String authorize = Base64.encode( + String.format("%s:%s", service.getApiKey(), service.getApiSecret()).getBytes(Charset.forName("UTF-8"))); - assertEquals(OAuthConstants.BASIC + " " + authorize, map.get(OAuthConstants.HEADER)); + assertEquals(OAuthConstants.BASIC + ' ' + authorize, response.get(OAuthConstants.HEADER).asText()); - assertEquals("user1", map.get("query-username")); - assertEquals("password1", map.get("query-password")); - assertEquals("password", map.get("query-grant_type")); + assertEquals("user1", response.get("query-username").asText()); + assertEquals("password1", response.get("query-password").asText()); + assertEquals("password", response.get("query-grant_type").asText()); } @Test @@ -119,13 +113,5 @@ public void testOAuthExtractAuthorization() { authorization = service.extractAuthorization("https://cl.ex.com/cb"); assertEquals(null, authorization.getCode()); assertEquals(null, authorization.getState()); - } - - private static class TypeTokenImpl extends TypeToken> { - - private TypeTokenImpl() { - } - } - } diff --git a/scribejava-core/src/test/java/com/github/scribejava/core/oauth/OAuth20ServiceUnit.java b/scribejava-core/src/test/java/com/github/scribejava/core/oauth/OAuth20ServiceUnit.java index 8a49c0ee1..2766b7f2b 100644 --- a/scribejava-core/src/test/java/com/github/scribejava/core/oauth/OAuth20ServiceUnit.java +++ b/scribejava-core/src/test/java/com/github/scribejava/core/oauth/OAuth20ServiceUnit.java @@ -1,14 +1,17 @@ package com.github.scribejava.core.oauth; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.github.scribejava.core.builder.api.DefaultApi20; +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.OAuthConfig; import com.github.scribejava.core.model.OAuthConstants; import com.github.scribejava.core.model.OAuthRequest; import com.github.scribejava.core.model.Parameter; -import com.google.gson.Gson; +import java.io.OutputStream; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Future; @@ -17,10 +20,14 @@ class OAuth20ServiceUnit extends OAuth20Service { static final String TOKEN = "ae82980abab675c646a070686d5558ad"; static final String STATE = "123"; - static final String EXPIRES = "3600"; + static final int EXPIRES = 3600; + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - OAuth20ServiceUnit(DefaultApi20 api, OAuthConfig config) { - super(api, config); + OAuth20ServiceUnit(DefaultApi20 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); } @Override @@ -29,8 +36,7 @@ protected OAuth2AccessToken sendAccessTokenRequestSync(OAuthRequest request) { } private String prepareRawResponse(OAuthRequest request) { - final Gson json = new Gson(); - final Map response = new HashMap<>(); + final Map response = new HashMap<>(); response.put(OAuthConstants.ACCESS_TOKEN, TOKEN); response.put(OAuthConstants.STATE, STATE); response.put("expires_in", EXPIRES); @@ -38,11 +44,15 @@ private String prepareRawResponse(OAuthRequest request) { response.putAll(request.getHeaders()); response.putAll(request.getOauthParameters()); - for (Parameter p : request.getBodyParams().getParams()) { - response.put("query-" + p.getKey(), p.getValue()); + for (Parameter param : request.getBodyParams().getParams()) { + response.put("query-" + param.getKey(), param.getValue()); } - return json.toJson(response); + try { + return OBJECT_MAPPER.writeValueAsString(response); + } catch (JsonProcessingException ex) { + throw new IllegalStateException("smth wrong with Jackson?"); + } } @Override diff --git a/scribejava-core/src/test/java/com/github/scribejava/core/pkce/PKCECodeChallengeMethodTest.java b/scribejava-core/src/test/java/com/github/scribejava/core/pkce/PKCECodeChallengeMethodTest.java new file mode 100644 index 000000000..a83a5470d --- /dev/null +++ b/scribejava-core/src/test/java/com/github/scribejava/core/pkce/PKCECodeChallengeMethodTest.java @@ -0,0 +1,26 @@ +package com.github.scribejava.core.pkce; + +import org.junit.Test; +import static org.junit.Assert.assertEquals; + +/** + * test PKCE according to
+ * Appendix B. Example for the S256 code_challenge_method
+ * https://tools.ietf.org/html/rfc7636#appendix-B + */ +public class PKCECodeChallengeMethodTest { + + private static final byte[] RANDOM_BYTES = new byte[]{116, 24, (byte) 223, (byte) 180, (byte) 151, (byte) 153, + (byte) 224, 37, 79, (byte) 250, 96, 125, (byte) 216, (byte) 173, (byte) 187, (byte) 186, 22, (byte) 212, 37, 77, + 105, (byte) 214, (byte) 191, (byte) 240, 91, 88, 5, 88, 83, (byte) 132, (byte) 141, 121}; + + @Test + public void testGeneratingPKCE() { + final PKCE pkce = PKCEService.defaultInstance().generatePKCE(RANDOM_BYTES); + + assertEquals(PKCECodeChallengeMethod.S256, pkce.getCodeChallengeMethod()); + assertEquals("dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk", pkce.getCodeVerifier()); + assertEquals("E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", pkce.getCodeChallenge()); + } + +} 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 261bc2bb4..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 @@ -5,7 +5,7 @@ import java.security.PrivateKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; -import javax.xml.bind.DatatypeConverter; +import org.apache.commons.codec.binary.Base64; import static org.junit.Assert.assertEquals; import org.junit.Test; @@ -48,11 +48,12 @@ private static PrivateKey getPrivateKey() { + "I/AfAkEA0Y9vr0tombsUB8cZv0v5OYoBZvCTbMANtzfb4AOHpiKqqbohDOevLQ7/\n" + "SpvgVCmVaDz2PptcRAyEBZ5MCssneQJAB2pmvaDH7Ambfod5bztLfOhLCtY5EkXJ\n" + "n6rZcDbRaHorRhdG7m3VtDKOUKZ2DF7glkQGV33phKukErVPUzlHBwJAScD9TqaG\n" - + "wJ3juUsVtujV23SnH43iMggXT7m82STpPGam1hPfmqu2Z0niePFo927ogQ7H1EMJ\n" + "UHgqXmuvk2X/Ww=="; + + "wJ3juUsVtujV23SnH43iMggXT7m82STpPGam1hPfmqu2Z0niePFo927ogQ7H1EMJ\n" + + "UHgqXmuvk2X/Ww=="; try { final KeyFactory fac = KeyFactory.getInstance("RSA"); - final PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(DatatypeConverter.parseBase64Binary(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/MapUtilsTest.java b/scribejava-core/src/test/java/com/github/scribejava/core/utils/MapUtilsTest.java deleted file mode 100644 index 9ce36c76f..000000000 --- a/scribejava-core/src/test/java/com/github/scribejava/core/utils/MapUtilsTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.github.scribejava.core.utils; - -import java.util.LinkedHashMap; -import java.util.HashMap; -import java.util.Map; -import org.junit.Assert; -import org.junit.Test; - -public class MapUtilsTest { - - @Test - public void shouldPrettyPrintMap() { - final Map map = new LinkedHashMap<>(); - map.put(1, "one"); - map.put(2, "two"); - map.put(3, "three"); - map.put(4, "four"); - Assert.assertEquals("{ 1 -> one , 2 -> two , 3 -> three , 4 -> four }", MapUtils.toString(map)); - } - - @Test - public void shouldHandleEmptyMap() { - final Map map = new HashMap<>(); - Assert.assertEquals("{}", MapUtils.toString(map)); - } - - @Test - public void shouldHandleNullInputs() { - Assert.assertEquals("", MapUtils.toString(null)); - } -} 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 a97c6a073..fabd225c3 100644 --- a/scribejava-httpclient-ahc/pom.xml +++ b/scribejava-httpclient-ahc/pom.xml @@ -5,10 +5,10 @@ com.github.scribejava scribejava - 4.1.3-SNAPSHOT + 8.3.4-SNAPSHOT ../pom.xml - + com.github.scribejava scribejava-httpclient-ahc ScribeJava Async Http Http Client support @@ -20,10 +20,23 @@ scribejava-core ${project.version} + + org.slf4j + slf4j-simple + 2.0.3 + test + org.asynchttpclient async-http-client - 2.0.33 + 2.12.3 + + + com.github.scribejava + scribejava-core + ${project.version} + test-jar + test 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 cdc894bb8..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 @@ -1,6 +1,7 @@ package com.github.scribejava.httpclient.ahc; import com.github.scribejava.core.httpclient.AbstractAsyncOnlyHttpClient; +import com.github.scribejava.core.httpclient.multipart.MultipartPayload; import com.github.scribejava.core.model.OAuthAsyncRequestCallback; import com.github.scribejava.core.model.OAuthConstants; import com.github.scribejava.core.model.OAuthRequest; @@ -20,6 +21,10 @@ public class AhcHttpClient extends AbstractAsyncOnlyHttpClient { private final AsyncHttpClient client; + public AhcHttpClient() { + this(AhcHttpClientConfig.defaultConfig()); + } + public AhcHttpClient(AhcHttpClientConfig ahcConfig) { final AsyncHttpClientConfig clientConfig = ahcConfig.getClientConfig(); client = clientConfig == null ? new DefaultAsyncHttpClient() : new DefaultAsyncHttpClient(clientConfig); @@ -41,6 +46,14 @@ public Future executeAsync(String userAgent, Map headers, converter); } + @Override + public Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, + MultipartPayload bodyContents, OAuthAsyncRequestCallback callback, + OAuthRequest.ResponseConverter converter) { + + throw new UnsupportedOperationException("AhcHttpClient does not support MultipartPayload yet."); + } + @Override public Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, String bodyContents, OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter) { @@ -58,7 +71,7 @@ public Future executeAsync(String userAgent, Map headers, private Future doExecuteAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, BodySetter bodySetter, Object bodyContents, OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter) { - BoundRequestBuilder boundRequestBuilder; + final BoundRequestBuilder boundRequestBuilder; switch (httpVerb) { case GET: boundRequestBuilder = client.prepareGet(completeUrl); @@ -78,14 +91,15 @@ private Future doExecuteAsync(String userAgent, Map heade if (httpVerb.isPermitBody()) { if (!headers.containsKey(CONTENT_TYPE)) { - boundRequestBuilder = boundRequestBuilder.addHeader(CONTENT_TYPE, DEFAULT_CONTENT_TYPE); + boundRequestBuilder.addHeader(CONTENT_TYPE, DEFAULT_CONTENT_TYPE); } - boundRequestBuilder = bodySetter.setBody(boundRequestBuilder, bodyContents); + bodySetter.setBody(boundRequestBuilder, bodyContents); } for (Map.Entry header : headers.entrySet()) { boundRequestBuilder.addHeader(header.getKey(), header.getValue()); } + if (userAgent != null) { boundRequestBuilder.setHeader(OAuthConstants.USER_AGENT_HEADER_NAME, userAgent); } diff --git a/scribejava-httpclient-ahc/src/main/java/com/github/scribejava/httpclient/ahc/OAuthAsyncCompletionHandler.java b/scribejava-httpclient-ahc/src/main/java/com/github/scribejava/httpclient/ahc/OAuthAsyncCompletionHandler.java index 856cbcf68..0cb77af25 100644 --- a/scribejava-httpclient-ahc/src/main/java/com/github/scribejava/httpclient/ahc/OAuthAsyncCompletionHandler.java +++ b/scribejava-httpclient-ahc/src/main/java/com/github/scribejava/httpclient/ahc/OAuthAsyncCompletionHandler.java @@ -3,7 +3,6 @@ import com.github.scribejava.core.model.OAuthAsyncRequestCallback; import com.github.scribejava.core.model.OAuthRequest; import com.github.scribejava.core.model.Response; -import io.netty.handler.codec.http.HttpHeaders; import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -21,21 +20,26 @@ public OAuthAsyncCompletionHandler(OAuthAsyncRequestCallback callback, } @Override - public T onCompleted(org.asynchttpclient.Response ahcResponse) throws IOException { - final HttpHeaders map = ahcResponse.getHeaders(); - final Map headersMap = new HashMap<>(); - for (Map.Entry header : map) { - headersMap.put(header.getKey(), header.getValue()); - } - final Response response = new Response(ahcResponse.getStatusCode(), ahcResponse.getStatusText(), headersMap, - ahcResponse.getResponseBodyAsStream()); + public T onCompleted(org.asynchttpclient.Response ahcResponse) { + try { + final Map headersMap = new HashMap<>(); + for (Map.Entry header : ahcResponse.getHeaders()) { + headersMap.put(header.getKey(), header.getValue()); + } - @SuppressWarnings("unchecked") - final T t = converter == null ? (T) response : converter.convert(response); - if (callback != null) { - callback.onCompleted(t); + final Response response = new Response(ahcResponse.getStatusCode(), ahcResponse.getStatusText(), headersMap, + ahcResponse.getResponseBodyAsStream()); + + @SuppressWarnings("unchecked") + final T t = converter == null ? (T) response : converter.convert(response); + if (callback != null) { + callback.onCompleted(t); + } + return t; + } catch (IOException | RuntimeException e) { + onThrowable(e); + return null; } - return t; } @Override @@ -44,4 +48,4 @@ public void onThrowable(Throwable t) { callback.onThrowable(t); } } -}; +} diff --git a/scribejava-httpclient-ahc/src/test/java/com/github/scribejava/httpclient/ahc/AhcHttpClientTest.java b/scribejava-httpclient-ahc/src/test/java/com/github/scribejava/httpclient/ahc/AhcHttpClientTest.java new file mode 100644 index 000000000..5de2d153e --- /dev/null +++ b/scribejava-httpclient-ahc/src/test/java/com/github/scribejava/httpclient/ahc/AhcHttpClientTest.java @@ -0,0 +1,12 @@ +package com.github.scribejava.httpclient.ahc; + +import com.github.scribejava.core.AbstractClientTest; +import com.github.scribejava.core.httpclient.HttpClient; + +public class AhcHttpClientTest extends AbstractClientTest { + + @Override + protected HttpClient createNewClient() { + return new AhcHttpClient(); + } +} diff --git a/scribejava-httpclient-apache/pom.xml b/scribejava-httpclient-apache/pom.xml new file mode 100644 index 000000000..1dc9dd53c --- /dev/null +++ b/scribejava-httpclient-apache/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + + com.github.scribejava + scribejava + 8.3.4-SNAPSHOT + ../pom.xml + + + com.github.scribejava + scribejava-httpclient-apache + ScribeJava Apache HttpComponents HttpClient support + jar + + + + com.github.scribejava + scribejava-core + ${project.version} + + + org.apache.httpcomponents + httpclient + 4.5.13 + + + org.apache.httpcomponents + httpasyncclient + 4.1.5 + + + com.github.scribejava + scribejava-core + ${project.version} + test-jar + test + + + + + + + org.apache.felix + maven-bundle-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + + diff --git a/scribejava-httpclient-apache/src/main/java/com/github/scribejava/httpclient/apache/ApacheHttpClient.java b/scribejava-httpclient-apache/src/main/java/com/github/scribejava/httpclient/apache/ApacheHttpClient.java new file mode 100644 index 000000000..8c204e67b --- /dev/null +++ b/scribejava-httpclient-apache/src/main/java/com/github/scribejava/httpclient/apache/ApacheHttpClient.java @@ -0,0 +1,126 @@ +package com.github.scribejava.httpclient.apache; + +import com.github.scribejava.core.httpclient.AbstractAsyncOnlyHttpClient; +import com.github.scribejava.core.httpclient.multipart.MultipartPayload; +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 org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.RequestBuilder; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.entity.FileEntity; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.concurrent.Future; +import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; + +public class ApacheHttpClient extends AbstractAsyncOnlyHttpClient { + + private final CloseableHttpAsyncClient client; + + public ApacheHttpClient() { + this(ApacheHttpClientConfig.defaultConfig()); + } + + public ApacheHttpClient(ApacheHttpClientConfig config) { + this(config.getHttpAsyncClientBuilder()); + } + + public ApacheHttpClient(HttpAsyncClientBuilder builder) { + this(builder.build()); + } + + public ApacheHttpClient(CloseableHttpAsyncClient client) { + this.client = client; + this.client.start(); + } + + @Override + public void close() throws IOException { + client.close(); + } + + @Override + public Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, + byte[] bodyContents, OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter) { + final HttpEntity entity = bodyContents == null ? null : new ByteArrayEntity(bodyContents); + return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, entity, callback, converter); + } + + @Override + public Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, + MultipartPayload bodyContents, OAuthAsyncRequestCallback callback, + OAuthRequest.ResponseConverter converter) { + + throw new UnsupportedOperationException("ApacheHttpClient does not support MultipartPayload yet."); + } + + @Override + public Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, + String bodyContents, OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter) { + final HttpEntity entity = bodyContents == null ? null : new StringEntity(bodyContents, StandardCharsets.UTF_8); + return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, entity, callback, converter); + } + + @Override + public Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, + File bodyContents, OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter) { + final HttpEntity entity = bodyContents == null ? null : new FileEntity(bodyContents); + return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, entity, callback, converter); + } + + private Future doExecuteAsync(String userAgent, Map headers, Verb httpVerb, + String completeUrl, HttpEntity entity, OAuthAsyncRequestCallback callback, + OAuthRequest.ResponseConverter converter) { + final RequestBuilder builder = getRequestBuilder(httpVerb); + builder.setUri(completeUrl); + + if (httpVerb.isPermitBody()) { + if (!headers.containsKey(CONTENT_TYPE)) { + builder.addHeader(CONTENT_TYPE, DEFAULT_CONTENT_TYPE); + } + builder.setEntity(entity); + } + + for (Map.Entry header : headers.entrySet()) { + builder.addHeader(header.getKey(), header.getValue()); + } + + if (userAgent != null) { + builder.setHeader(OAuthConstants.USER_AGENT_HEADER_NAME, userAgent); + } + final OAuthAsyncCompletionHandler handler = new OAuthAsyncCompletionHandler<>(callback, converter); + final Future future = client.execute(builder.build(), handler); + return new ApacheHttpFuture<>(future, handler); + } + + private static RequestBuilder getRequestBuilder(Verb httpVerb) { + switch (httpVerb) { + case GET: + return RequestBuilder.get(); + case PUT: + return RequestBuilder.put(); + case DELETE: + return RequestBuilder.delete(); + case HEAD: + return RequestBuilder.head(); + case POST: + return RequestBuilder.post(); + case PATCH: + return RequestBuilder.patch(); + case TRACE: + return RequestBuilder.trace(); + case OPTIONS: + return RequestBuilder.options(); + default: + throw new IllegalArgumentException("message build error: unknown verb type"); + } + } +} diff --git a/scribejava-httpclient-apache/src/main/java/com/github/scribejava/httpclient/apache/ApacheHttpClientConfig.java b/scribejava-httpclient-apache/src/main/java/com/github/scribejava/httpclient/apache/ApacheHttpClientConfig.java new file mode 100644 index 000000000..2a4f3a5b0 --- /dev/null +++ b/scribejava-httpclient-apache/src/main/java/com/github/scribejava/httpclient/apache/ApacheHttpClientConfig.java @@ -0,0 +1,26 @@ +package com.github.scribejava.httpclient.apache; + +import com.github.scribejava.core.httpclient.HttpClientConfig; +import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; + +public class ApacheHttpClientConfig implements HttpClientConfig { + + private final HttpAsyncClientBuilder httpAsyncClientBuilder; + + public ApacheHttpClientConfig(HttpAsyncClientBuilder httpAsyncClientBuilder) { + this.httpAsyncClientBuilder = httpAsyncClientBuilder; + } + + public HttpAsyncClientBuilder getHttpAsyncClientBuilder() { + return httpAsyncClientBuilder; + } + + @Override + public HttpClientConfig createDefaultConfig() { + return defaultConfig(); + } + + public static ApacheHttpClientConfig defaultConfig() { + return new ApacheHttpClientConfig(HttpAsyncClientBuilder.create()); + } +} diff --git a/scribejava-httpclient-apache/src/main/java/com/github/scribejava/httpclient/apache/ApacheHttpFuture.java b/scribejava-httpclient-apache/src/main/java/com/github/scribejava/httpclient/apache/ApacheHttpFuture.java new file mode 100644 index 000000000..47c098383 --- /dev/null +++ b/scribejava-httpclient-apache/src/main/java/com/github/scribejava/httpclient/apache/ApacheHttpFuture.java @@ -0,0 +1,44 @@ +package com.github.scribejava.httpclient.apache; + +import org.apache.http.HttpResponse; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class ApacheHttpFuture implements Future { + + private final Future future; + private final OAuthAsyncCompletionHandler handler; + + public ApacheHttpFuture(Future future, OAuthAsyncCompletionHandler handler) { + this.future = future; + this.handler = handler; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return future.cancel(mayInterruptIfRunning); + } + + @Override + public boolean isCancelled() { + return future.isCancelled(); + } + + @Override + public boolean isDone() { + return future.isDone(); + } + + @Override + public T get() throws InterruptedException, ExecutionException { + return handler.getResult(); + } + + @Override + public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + return handler.getResult(timeout, unit); + } +} diff --git a/scribejava-httpclient-apache/src/main/java/com/github/scribejava/httpclient/apache/ApacheProvider.java b/scribejava-httpclient-apache/src/main/java/com/github/scribejava/httpclient/apache/ApacheProvider.java new file mode 100644 index 000000000..553972d1d --- /dev/null +++ b/scribejava-httpclient-apache/src/main/java/com/github/scribejava/httpclient/apache/ApacheProvider.java @@ -0,0 +1,16 @@ +package com.github.scribejava.httpclient.apache; + +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; +import com.github.scribejava.core.httpclient.HttpClientProvider; + +public class ApacheProvider implements HttpClientProvider { + + @Override + public HttpClient createClient(HttpClientConfig httpClientConfig) { + if (httpClientConfig instanceof ApacheHttpClientConfig) { + return new ApacheHttpClient((ApacheHttpClientConfig) httpClientConfig); + } + return null; + } +} diff --git a/scribejava-httpclient-apache/src/main/java/com/github/scribejava/httpclient/apache/OAuthAsyncCompletionHandler.java b/scribejava-httpclient-apache/src/main/java/com/github/scribejava/httpclient/apache/OAuthAsyncCompletionHandler.java new file mode 100644 index 000000000..7779b83e7 --- /dev/null +++ b/scribejava-httpclient-apache/src/main/java/com/github/scribejava/httpclient/apache/OAuthAsyncCompletionHandler.java @@ -0,0 +1,110 @@ +package com.github.scribejava.httpclient.apache; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.apache.http.StatusLine; +import org.apache.http.concurrent.FutureCallback; + +import com.github.scribejava.core.model.OAuthAsyncRequestCallback; +import com.github.scribejava.core.model.OAuthRequest.ResponseConverter; +import com.github.scribejava.core.model.Response; +import java.io.IOException; +import java.io.InputStream; +import org.apache.http.HttpEntity; + +public class OAuthAsyncCompletionHandler implements FutureCallback { + + private final OAuthAsyncRequestCallback callback; + private final ResponseConverter converter; + private final CountDownLatch latch; + private T result; + private Exception exception; + + public OAuthAsyncCompletionHandler(OAuthAsyncRequestCallback callback, ResponseConverter converter) { + this.callback = callback; + this.converter = converter; + this.latch = new CountDownLatch(1); + } + + @Override + public void completed(HttpResponse httpResponse) { + try { + final Map headersMap = new HashMap<>(); + for (Header header : httpResponse.getAllHeaders()) { + headersMap.put(header.getName(), header.getValue()); + } + + final StatusLine statusLine = httpResponse.getStatusLine(); + + final HttpEntity httpEntity = httpResponse.getEntity(); + final InputStream contentStream = httpEntity == null ? null : httpEntity.getContent(); + final Response response = new Response(statusLine.getStatusCode(), statusLine.getReasonPhrase(), headersMap, + contentStream, contentStream); + + @SuppressWarnings("unchecked") + final T t = converter == null ? (T) response : converter.convert(response); + result = t; + if (callback != null) { + callback.onCompleted(result); + } + } catch (IOException | RuntimeException e) { + exception = e; + if (callback != null) { + callback.onThrowable(e); + } + } finally { + latch.countDown(); + } + } + + @Override + public void failed(Exception e) { + exception = e; + try { + if (callback != null) { + callback.onThrowable(e); + } + } finally { + latch.countDown(); + } + } + + @Override + public void cancelled() { + exception = new CancellationException(); + try { + if (callback != null) { + callback.onThrowable(exception); + } + } finally { + latch.countDown(); + } + } + + public T getResult() throws InterruptedException, ExecutionException { + latch.await(); + if (exception != null) { + throw new ExecutionException(exception); + } + return result; + } + + public T getResult(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + + if (!latch.await(timeout, unit)) { + throw new TimeoutException(); + } + if (exception != null) { + throw new ExecutionException(exception); + } + return result; + } +} diff --git a/scribejava-httpclient-apache/src/main/resources/META-INF/services/com.github.scribejava.core.httpclient.HttpClientProvider b/scribejava-httpclient-apache/src/main/resources/META-INF/services/com.github.scribejava.core.httpclient.HttpClientProvider new file mode 100644 index 000000000..651910028 --- /dev/null +++ b/scribejava-httpclient-apache/src/main/resources/META-INF/services/com.github.scribejava.core.httpclient.HttpClientProvider @@ -0,0 +1 @@ +com.github.scribejava.httpclient.apache.ApacheProvider diff --git a/scribejava-httpclient-apache/src/test/java/com/github/scribejava/httpclient/apache/ApacheHttpClientTest.java b/scribejava-httpclient-apache/src/test/java/com/github/scribejava/httpclient/apache/ApacheHttpClientTest.java new file mode 100644 index 000000000..03924d81e --- /dev/null +++ b/scribejava-httpclient-apache/src/test/java/com/github/scribejava/httpclient/apache/ApacheHttpClientTest.java @@ -0,0 +1,13 @@ +package com.github.scribejava.httpclient.apache; + +import com.github.scribejava.core.AbstractClientTest; +import com.github.scribejava.core.httpclient.HttpClient; + +public class ApacheHttpClientTest extends AbstractClientTest { + + @Override + protected HttpClient createNewClient() { + return new ApacheHttpClient(); + } + +} 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 new file mode 100644 index 000000000..684ac5c62 --- /dev/null +++ b/scribejava-httpclient-apache/src/test/java/com/github/scribejava/httpclient/apache/OAuthAsyncCompletionHandlerTest.java @@ -0,0 +1,193 @@ +package com.github.scribejava.httpclient.apache; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; + +import org.apache.http.HttpResponse; +import org.apache.http.ProtocolVersion; +import org.apache.http.entity.BasicHttpEntity; +import org.apache.http.message.BasicHttpResponse; +import org.apache.http.message.BasicStatusLine; +import org.junit.Before; +import org.junit.Test; + +import com.github.scribejava.core.exceptions.OAuthException; +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 { + + private static final AllGoodResponseConverter ALL_GOOD_RESPONSE_CONVERTER = new AllGoodResponseConverter(); + private static final ExceptionResponseConverter EXCEPTION_RESPONSE_CONVERTER = new ExceptionResponseConverter(); + private static final OAuthExceptionResponseConverter OAUTH_EXCEPTION_RESPONSE_CONVERTER + = new OAuthExceptionResponseConverter(); + + private OAuthAsyncCompletionHandler handler; + private TestCallback callback; + + private static class TestCallback implements OAuthAsyncRequestCallback { + + private Throwable throwable; + private String response; + + @Override + public void onCompleted(String response) { + this.response = response; + } + + @Override + public void onThrowable(Throwable throwable) { + this.throwable = throwable; + } + + public Throwable getThrowable() { + return throwable; + } + + public String getResponse() { + return response; + } + + } + + @Before + public void setUp() { + callback = new TestCallback(); + } + + @Test + public void shouldReleaseLatchOnSuccess() throws Exception { + handler = new OAuthAsyncCompletionHandler<>(callback, ALL_GOOD_RESPONSE_CONVERTER); + final HttpResponse response + = new BasicHttpResponse(new BasicStatusLine(new ProtocolVersion("4", 1, 1), 200, "ok")); + final BasicHttpEntity entity = new BasicHttpEntity(); + entity.setContent(new ByteArrayInputStream(new byte[0])); + response.setEntity(entity); + handler.completed(response); + assertNotNull(callback.getResponse()); + assertNull(callback.getThrowable()); + // verify latch is released + assertEquals("All good", handler.getResult()); + } + + @Test + public void shouldReleaseLatchOnIOException() { + handler = new OAuthAsyncCompletionHandler<>(callback, EXCEPTION_RESPONSE_CONVERTER); + final HttpResponse response + = new BasicHttpResponse(new BasicStatusLine(new ProtocolVersion("4", 1, 1), 200, "ok")); + final BasicHttpEntity entity = new BasicHttpEntity(); + entity.setContent(new ByteArrayInputStream(new byte[0])); + response.setEntity(entity); + handler.completed(response); + assertNull(callback.getResponse()); + assertNotNull(callback.getThrowable()); + assertTrue(callback.getThrowable() instanceof IOException); + // verify latch is released + assertThrows(ExecutionException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + handler.getResult(); + } + }); + } + + @Test + 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")); + final BasicHttpEntity entity = new BasicHttpEntity(); + entity.setContent(new ByteArrayInputStream(new byte[0])); + response.setEntity(entity); + handler.completed(response); + assertNull(callback.getResponse()); + assertNotNull(callback.getThrowable()); + assertTrue(callback.getThrowable() instanceof OAuthException); + // verify latch is released + assertThrows(ExecutionException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + handler.getResult(); + } + }); + } + + @Test + 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")); + final BasicHttpEntity entity = new BasicHttpEntity(); + entity.setContent(new ByteArrayInputStream(new byte[0])); + response.setEntity(entity); + handler.cancelled(); + assertNull(callback.getResponse()); + assertNotNull(callback.getThrowable()); + assertTrue(callback.getThrowable() instanceof CancellationException); + // verify latch is released + assertThrows(ExecutionException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + handler.getResult(); + } + }); + } + + @Test + 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")); + final BasicHttpEntity entity = new BasicHttpEntity(); + entity.setContent(new ByteArrayInputStream(new byte[0])); + response.setEntity(entity); + handler.failed(new RuntimeException()); + assertNull(callback.getResponse()); + assertNotNull(callback.getThrowable()); + assertTrue(callback.getThrowable() instanceof RuntimeException); + // verify latch is released + assertThrows(ExecutionException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + handler.getResult(); + } + }); + } + + private static class AllGoodResponseConverter implements OAuthRequest.ResponseConverter { + + @Override + public String convert(Response response) throws IOException { + response.close(); + return "All good"; + } + } + + private static class ExceptionResponseConverter implements OAuthRequest.ResponseConverter { + + @Override + public String convert(Response response) throws IOException { + response.close(); + throw new IOException("Failed to convert"); + } + } + + private static class OAuthExceptionResponseConverter implements OAuthRequest.ResponseConverter { + + @Override + public String convert(Response response) throws IOException { + response.close(); + throw new OAuthException("bad oauth"); + } + } +} diff --git a/scribejava-httpclient-armeria/pom.xml b/scribejava-httpclient-armeria/pom.xml new file mode 100644 index 000000000..54e58c619 --- /dev/null +++ b/scribejava-httpclient-armeria/pom.xml @@ -0,0 +1,59 @@ + + + 4.0.0 + + + com.github.scribejava + scribejava + 8.3.4-SNAPSHOT + ../pom.xml + + + com.github.scribejava + scribejava-httpclient-armeria + ScribeJava Async Armeria Client support + jar + + + + com.github.scribejava + scribejava-core + ${project.version} + + + org.slf4j + slf4j-simple + 2.0.3 + test + + + com.linecorp.armeria + armeria + 1.20.2 + + + com.github.scribejava + scribejava-core + ${project.version} + test-jar + test + + + + + + + org.apache.felix + maven-bundle-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + + + + 8 + + diff --git a/scribejava-httpclient-armeria/src/main/java/com/github/scribejava/httpclient/armeria/ArmeriaHttpClient.java b/scribejava-httpclient-armeria/src/main/java/com/github/scribejava/httpclient/armeria/ArmeriaHttpClient.java new file mode 100644 index 000000000..982ddc63a --- /dev/null +++ b/scribejava-httpclient-armeria/src/main/java/com/github/scribejava/httpclient/armeria/ArmeriaHttpClient.java @@ -0,0 +1,375 @@ +package com.github.scribejava.httpclient.armeria; + +import static java.util.Objects.requireNonNull; + +import com.github.scribejava.core.httpclient.AbstractAsyncOnlyHttpClient; +import com.github.scribejava.core.httpclient.multipart.MultipartPayload; +import com.github.scribejava.core.httpclient.multipart.MultipartUtils; +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.Response; +import com.github.scribejava.core.model.Verb; +import com.linecorp.armeria.client.WebClient; +import com.linecorp.armeria.common.AggregatedHttpResponse; +import com.linecorp.armeria.common.HttpData; +import com.linecorp.armeria.common.HttpMethod; +import com.linecorp.armeria.common.HttpResponse; +import com.linecorp.armeria.common.HttpStatus; +import com.linecorp.armeria.common.MediaType; +import com.linecorp.armeria.common.RequestHeaders; +import com.linecorp.armeria.common.RequestHeadersBuilder; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.nio.file.Files; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Supplier; + +/** + * An implementation of {@link AbstractAsyncOnlyHttpClient} based on + * Armeria HTTP client. + */ +public class ArmeriaHttpClient extends AbstractAsyncOnlyHttpClient { + + /** + * A builder of new instances of Armeria's {@link WebClient} + */ + private final ArmeriaWebClientBuilder clientBuilder; + /** + * A list of cached Endpoints. It helps avoiding building a new Endpoint per each request. + */ + private final Map httpClients = new HashMap<>(); + /** + * A read/write lock to access the list of cached Endpoints concurrently. + */ + private final ReentrantReadWriteLock httpClientsLock = new ReentrantReadWriteLock(); + + public ArmeriaHttpClient() { + this(ArmeriaHttpClientConfig.defaultConfig()); + } + + public ArmeriaHttpClient(ArmeriaHttpClientConfig config) { + clientBuilder = config.createClientBuilder(); + } + + /** + * Cleans up the list of cached Endpoints. + */ + @Override + public void close() { + final Lock writeLock = httpClientsLock.writeLock(); + writeLock.lock(); + try { + httpClients.clear(); + } finally { + writeLock.unlock(); + } + } + + @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 BytesBody(bodyContents), callback, + converter); + } + + @Override + public Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, + MultipartPayload bodyContents, OAuthAsyncRequestCallback callback, + OAuthRequest.ResponseConverter converter) { + return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, new MultipartBody(bodyContents), callback, + converter); + } + + @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 StringBody(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 FileBody(bodyContents), callback, + converter); + } + + private CompletableFuture doExecuteAsync(String userAgent, Map headers, Verb httpVerb, + String completeUrl, Supplier contentSupplier, OAuthAsyncRequestCallback callback, + OAuthRequest.ResponseConverter converter) { + // Get the URI and Path + final URI uri = URI.create(completeUrl); + final String path = getServicePath(uri); + + // Fetch/Create WebClient instance for a given Endpoint + final WebClient client = getClient(uri); + + // Build HTTP request + final RequestHeadersBuilder headersBuilder = RequestHeaders.of(getHttpMethod(httpVerb), path).toBuilder(); + + headersBuilder.add(headers.entrySet()); + if (userAgent != null) { + headersBuilder.add(OAuthConstants.USER_AGENT_HEADER_NAME, userAgent); + } + + // Build the request body and execute HTTP request + final HttpResponse response; + if (httpVerb.isPermitBody()) { // POST, PUT, PATCH and DELETE methods + final HttpData contents = contentSupplier.get(); + if (httpVerb.isRequiresBody() && contents == null) { // POST or PUT methods + throw new IllegalArgumentException("Contents missing for request method " + httpVerb.name()); + } + + if (headersBuilder.contentType() == null) { + headersBuilder.contentType(MediaType.FORM_DATA); + } + + if (contents != null) { + response = client.execute(headersBuilder.build(), contents); + } else { + response = client.execute(headersBuilder.build()); + } + } else { + response = client.execute(headersBuilder.build()); + } + + // Aggregate HTTP response (asynchronously) and return the result Future + return response.aggregate() + .thenApply(aggregatedResponse -> whenResponseComplete(callback, converter, aggregatedResponse)) + .exceptionally(throwable -> completeExceptionally(callback, throwable)); + } + + /** + * Provides an instance of {@link WebClient} for a given endpoint {@link URI} based on an endpoint as + * {@code scheme://authority}. + * + * @param uri an endpoint {@link URI} + * @return {@link WebClient} instance + */ + private WebClient getClient(URI uri) { + final String endpoint = getEndPoint(uri); + + WebClient client; + final Lock readLock = httpClientsLock.readLock(); + readLock.lock(); + try { + client = httpClients.get(endpoint); + } finally { + readLock.unlock(); + } + + if (client != null) { + return client; + } + + client = clientBuilder.newWebClient( + requireNonNull(uri.getScheme(), "scheme"), + requireNonNull(uri.getAuthority(), "authority")); + + final Lock writeLock = httpClientsLock.writeLock(); + writeLock.lock(); + try { + if (!httpClients.containsKey(endpoint)) { + httpClients.put(endpoint, client); + return client; + } else { + return httpClients.get(endpoint); + } + } finally { + writeLock.unlock(); + } + } + + /** + * Extracts {@code scheme} and {@code authority} portion of the {@link URI}. + * + * Assuming the {@link URI} as the following: {@code URI = scheme:[//authority]path[?query][#fragment]} + */ + private static String getEndPoint(URI uri) { + return requireNonNull(uri.getScheme(), "scheme") + "://" + requireNonNull(uri.getAuthority(), "authority"); + } + + /** + * Extracts {@code path}, {@code query} and {@code fragment} portion of the {@link URI}. + * + * Assuming the {@link URI} as the following: {@code URI = scheme:[//authority]path[?query][#fragment]} + */ + private static String getServicePath(URI uri) { + final StringBuilder builder = new StringBuilder() + .append(requireNonNull(uri.getPath(), "path")); + final String query = uri.getQuery(); + if (query != null) { + builder.append('?').append(query); + } + final String fragment = uri.getFragment(); + if (fragment != null) { + builder.append('#').append(fragment); + } + return builder.toString(); + } + + /** + * Maps {@link Verb} to {@link HttpMethod} + * + * @param httpVerb a {@link Verb} to match with {@link HttpMethod} + * @return {@link HttpMethod} corresponding to the parameter + */ + private static HttpMethod getHttpMethod(Verb httpVerb) { + switch (httpVerb) { + case GET: + return HttpMethod.GET; + case POST: + return HttpMethod.POST; + case PUT: + return HttpMethod.PUT; + case DELETE: + return HttpMethod.DELETE; + case HEAD: + return HttpMethod.HEAD; + case OPTIONS: + return HttpMethod.OPTIONS; + case TRACE: + return HttpMethod.TRACE; + case PATCH: + return HttpMethod.PATCH; + default: + throw new IllegalArgumentException( + "message build error: unsupported HTTP method: " + httpVerb.name()); + } + } + + // Response asynchronous handlers + /** + * Converts {@link AggregatedHttpResponse} to {@link Response} + * + * @param aggregatedResponse an instance of {@link AggregatedHttpResponse} to convert to {@link Response} + * @return a {@link Response} converted from {@link AggregatedHttpResponse} + */ + private Response convertResponse(AggregatedHttpResponse aggregatedResponse) { + final Map headersMap = new HashMap<>(); + aggregatedResponse.headers().forEach((header, value) -> headersMap.put(header.toString(), value)); + + final HttpStatus status = aggregatedResponse.status(); + final InputStream inputStream = aggregatedResponse.content().toInputStream(); + + return new Response(status.code(), status.reasonPhrase(), headersMap, inputStream, inputStream); + } + + /** + * Converts {@link AggregatedHttpResponse} to {@link Response} upon its aggregation completion and invokes + * {@link OAuthAsyncRequestCallback} for it. + * + * @param callback a {@link OAuthAsyncRequestCallback} callback to invoke upon response completion + * @param converter an optional {@link OAuthRequest.ResponseConverter} result converter for {@link Response} + * @param aggregatedResponse a source {@link AggregatedHttpResponse} to handle + * @param converter {@link OAuthRequest.ResponseConverter} specific type or {@link Response} + * @return either instance of {@link Response} or converted result based on {@link OAuthRequest.ResponseConverter} + */ + private T whenResponseComplete(OAuthAsyncRequestCallback callback, + OAuthRequest.ResponseConverter converter, AggregatedHttpResponse aggregatedResponse) { + final Response response = convertResponse(aggregatedResponse); + try { + @SuppressWarnings("unchecked") + final T t = converter == null ? (T) response : converter.convert(response); + if (callback != null) { + callback.onCompleted(t); + } + return t; + } catch (IOException | RuntimeException e) { + return completeExceptionally(callback, e); + } + } + + /** + * Invokes {@link OAuthAsyncRequestCallback} upon {@link Throwable} error result + * + * @param callback a {@link OAuthAsyncRequestCallback} callback to invoke upon response completion + * @param throwable a {@link Throwable} error result + * @param converter {@link OAuthRequest.ResponseConverter} specific type or {@link Response} + * @return null + */ + private T completeExceptionally(OAuthAsyncRequestCallback callback, Throwable throwable) { + if (callback != null) { + callback.onThrowable(throwable); + } + return null; + } + + // Body type suppliers + private static class BytesBody implements Supplier { + + private final byte[] bodyContents; + + BytesBody(byte[] bodyContents) { + this.bodyContents = bodyContents; + } + + @Override + public HttpData get() { + return (bodyContents != null) ? HttpData.wrap(bodyContents) : null; + } + } + + private static class StringBody implements Supplier { + + private final String bodyContents; + + StringBody(String bodyContents) { + this.bodyContents = bodyContents; + } + + @Override + public HttpData get() { + return (bodyContents != null) ? HttpData.ofUtf8(bodyContents) : null; + } + } + + private static class FileBody implements Supplier { + + private final File bodyContents; + + FileBody(File bodyContents) { + this.bodyContents = bodyContents; + } + + @Override + public HttpData get() { + try { + return (bodyContents != null) + ? HttpData.wrap(Files.readAllBytes(bodyContents.toPath())) + : null; + } catch (IOException ioE) { + throw new RuntimeException(ioE); + } + } + } + + private static class MultipartBody implements Supplier { + + private final MultipartPayload bodyContents; + + MultipartBody(MultipartPayload bodyContents) { + this.bodyContents = bodyContents; + } + + @Override + public HttpData get() { + try { + return (bodyContents != null) + ? HttpData.wrap(MultipartUtils.getPayload(bodyContents).toByteArray()) + : null; + } catch (IOException ioE) { + throw new RuntimeException(ioE); + } + } + } +} diff --git a/scribejava-httpclient-armeria/src/main/java/com/github/scribejava/httpclient/armeria/ArmeriaHttpClientConfig.java b/scribejava-httpclient-armeria/src/main/java/com/github/scribejava/httpclient/armeria/ArmeriaHttpClientConfig.java new file mode 100644 index 000000000..f9d494f52 --- /dev/null +++ b/scribejava-httpclient-armeria/src/main/java/com/github/scribejava/httpclient/armeria/ArmeriaHttpClientConfig.java @@ -0,0 +1,93 @@ +package com.github.scribejava.httpclient.armeria; + +import com.github.scribejava.core.httpclient.HttpClientConfig; +import com.linecorp.armeria.client.ClientFactory; +import com.linecorp.armeria.client.ClientOptions; +import com.linecorp.armeria.client.HttpClient; +import com.linecorp.armeria.client.logging.LoggingClient; +import com.linecorp.armeria.client.retry.RetryingClient; +import com.linecorp.armeria.common.SessionProtocol; +import java.util.function.Function; + +public class ArmeriaHttpClientConfig implements HttpClientConfig { + + private static final SessionProtocol DEFAULT_PROTOCOL_PREFERENCE = SessionProtocol.H1; // H1 or H2 + + private final ClientOptions clientOptions; + private final ClientFactory clientFactory; + private SessionProtocol protocolPreference; + private Function retry; + private Function logging; + + /** + * Creates new {@link ArmeriaHttpClientConfig} using provided {@link ClientOptions} and {@link ClientFactory}. + * + * @param clientOptions clientOptions + * @param clientFactory clientFactory + */ + public ArmeriaHttpClientConfig(ClientOptions clientOptions, ClientFactory clientFactory) { + this.clientOptions = clientOptions; + this.clientFactory = clientFactory; + protocolPreference = DEFAULT_PROTOCOL_PREFERENCE; + } + + /** + * Creates new {@link HttpClientConfig} using default settings. + * + * @return new {@link HttpClientConfig} using default settings. + */ + @Override + public HttpClientConfig createDefaultConfig() { + return defaultConfig(); + } + + /** + * Creates new {@link ArmeriaHttpClientConfig} using default settings. + * + * @return ArmeriaHttpClientConfig + */ + public static ArmeriaHttpClientConfig defaultConfig() { + return new ArmeriaHttpClientConfig(null, null); + } + + /** + * Selects which protocol shall take preference when generic protocol scheme used by the URL, like {@code http} or + * {@code https}. + * + * @param protocolPreference specifies which protocol shall take preference. Acceptable values: + * {@link SessionProtocol#H1} and {@link SessionProtocol#H2} + */ + public void setProtocolPreference(SessionProtocol protocolPreference) { + if (protocolPreference != SessionProtocol.H1 && protocolPreference != SessionProtocol.H2) { + throw new IllegalArgumentException("Invalid protocolPreference: " + protocolPreference); + } + this.protocolPreference = protocolPreference; + } + + public ArmeriaHttpClientConfig withProtocolPreference(SessionProtocol protocolPreference) { + setProtocolPreference(protocolPreference); + return this; + } + + public void setRetry(Function retry) { + this.retry = retry; + } + + public ArmeriaHttpClientConfig withRetry(Function retry) { + this.retry = retry; + return this; + } + + public void setLogging(Function logging) { + this.logging = logging; + } + + public ArmeriaHttpClientConfig withLogging(Function logging) { + this.logging = logging; + return this; + } + + ArmeriaWebClientBuilder createClientBuilder() { + return new ArmeriaWebClientBuilder(clientOptions, clientFactory, protocolPreference, retry, logging); + } +} diff --git a/scribejava-httpclient-armeria/src/main/java/com/github/scribejava/httpclient/armeria/ArmeriaProvider.java b/scribejava-httpclient-armeria/src/main/java/com/github/scribejava/httpclient/armeria/ArmeriaProvider.java new file mode 100644 index 000000000..323eb7599 --- /dev/null +++ b/scribejava-httpclient-armeria/src/main/java/com/github/scribejava/httpclient/armeria/ArmeriaProvider.java @@ -0,0 +1,16 @@ +package com.github.scribejava.httpclient.armeria; + +import com.github.scribejava.core.httpclient.HttpClientProvider; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; + +public class ArmeriaProvider implements HttpClientProvider { + + @Override + public HttpClient createClient(HttpClientConfig config) { + if (config instanceof ArmeriaHttpClientConfig) { + return new ArmeriaHttpClient((ArmeriaHttpClientConfig) config); + } + return null; + } +} diff --git a/scribejava-httpclient-armeria/src/main/java/com/github/scribejava/httpclient/armeria/ArmeriaWebClientBuilder.java b/scribejava-httpclient-armeria/src/main/java/com/github/scribejava/httpclient/armeria/ArmeriaWebClientBuilder.java new file mode 100644 index 000000000..6972fadd0 --- /dev/null +++ b/scribejava-httpclient-armeria/src/main/java/com/github/scribejava/httpclient/armeria/ArmeriaWebClientBuilder.java @@ -0,0 +1,74 @@ +package com.github.scribejava.httpclient.armeria; + +import com.linecorp.armeria.client.ClientFactory; +import com.linecorp.armeria.client.ClientOptions; +import com.linecorp.armeria.client.Endpoint; +import com.linecorp.armeria.client.HttpClient; +import com.linecorp.armeria.client.WebClient; +import com.linecorp.armeria.client.WebClientBuilder; +import com.linecorp.armeria.client.logging.LoggingClient; +import com.linecorp.armeria.client.retry.RetryingClient; +import com.linecorp.armeria.common.SessionProtocol; +import java.util.function.Function; + +/** + * A builder of {@link WebClient} using supplied configuration parameters. + */ +public class ArmeriaWebClientBuilder { + + private final ClientFactory clientFactory; + private final ClientOptions clientOptions; + private final SessionProtocol protocolPreference; + private final Function retry; + private final Function logging; + + ArmeriaWebClientBuilder(ClientOptions clientOptions, ClientFactory clientFactory, + SessionProtocol protocolPreference, Function retry, + Function logging) { + this.clientOptions = clientOptions; + this.clientFactory = clientFactory; + this.protocolPreference = protocolPreference; + this.retry = retry; + this.logging = logging; + } + + WebClient newWebClient(String scheme, String authority) { + final SessionProtocol protocol = protocol(scheme); + final Endpoint endpoint = Endpoint.parse(authority); + final WebClientBuilder clientBuilder = WebClient.builder(protocol, endpoint); + if (clientOptions != null) { + clientBuilder.options(clientOptions); + } + if (clientFactory != null) { + clientBuilder.factory(clientFactory); + } + if (retry != null) { + clientBuilder.decorator(retry); + } + if (logging != null) { + clientBuilder.decorator(logging); + } + return clientBuilder.build(); + } + + private SessionProtocol protocol(String scheme) { + final SessionProtocol protocol = SessionProtocol.of(scheme); + switch (protocol) { + case HTTP: + if (protocolPreference == SessionProtocol.H1) { + // enforce HTTP/1 protocol + return SessionProtocol.H1C; + } + break; + case HTTPS: + if (protocolPreference == SessionProtocol.H1) { + // enforce HTTP/1 protocol + return SessionProtocol.H1; + } + break; + default: + break; + } + return protocol; + } +} diff --git a/scribejava-httpclient-armeria/src/main/resources/META-INF/services/com.github.scribejava.core.httpclient.HttpClientProvider b/scribejava-httpclient-armeria/src/main/resources/META-INF/services/com.github.scribejava.core.httpclient.HttpClientProvider new file mode 100644 index 000000000..bba338db5 --- /dev/null +++ b/scribejava-httpclient-armeria/src/main/resources/META-INF/services/com.github.scribejava.core.httpclient.HttpClientProvider @@ -0,0 +1 @@ +com.github.scribejava.httpclient.armeria.ArmeriaProvider diff --git a/scribejava-httpclient-armeria/src/test/java/com/github/scribejava/httpclient/armeria/ArmeriaHttpClientTest.java b/scribejava-httpclient-armeria/src/test/java/com/github/scribejava/httpclient/armeria/ArmeriaHttpClientTest.java new file mode 100644 index 000000000..c94d6b369 --- /dev/null +++ b/scribejava-httpclient-armeria/src/test/java/com/github/scribejava/httpclient/armeria/ArmeriaHttpClientTest.java @@ -0,0 +1,88 @@ +package com.github.scribejava.httpclient.armeria; + +import com.github.scribejava.core.AbstractClientTest; +import com.github.scribejava.core.httpclient.HttpClient; +import com.linecorp.armeria.client.ClientFactory; +import com.linecorp.armeria.client.logging.LoggingClient; +import com.linecorp.armeria.client.retry.Backoff; +import com.linecorp.armeria.client.retry.RetryRule; +import com.linecorp.armeria.client.retry.RetryingClient; +import com.linecorp.armeria.common.HttpStatus; +import com.linecorp.armeria.common.logging.LogLevel; +import io.netty.channel.EventLoopGroup; +import io.netty.resolver.AbstractAddressResolver; +import io.netty.resolver.AddressResolver; +import io.netty.resolver.AddressResolverGroup; +import io.netty.util.concurrent.EventExecutor; +import io.netty.util.concurrent.Promise; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; +import org.slf4j.LoggerFactory; + +public class ArmeriaHttpClientTest extends AbstractClientTest { + + @Override + protected HttpClient createNewClient() { + // simulate DNS resolution for a mock address ("kubernetes.docker.internal") + final Function> addressRGF + = eventLoopGroup -> new MockAddressResolverGroup(); + // No-Op DNS resolver to avoid resolution issues in the unit test + final ClientFactory clientFactory = ClientFactory.builder().addressResolverGroupFactory(addressRGF).build(); + final ArmeriaHttpClientConfig config = new ArmeriaHttpClientConfig(null, clientFactory); + + // enable client-side HTTP tracing + config.setLogging(LoggingClient.builder() + .logger(LoggerFactory.getLogger("HTTP_TRACE")) + .requestLogLevel(LogLevel.valueOf("INFO")) + .successfulResponseLogLevel(LogLevel.valueOf("INFO")) + .failureResponseLogLevel(LogLevel.valueOf("WARN")) + .newDecorator()); + + // enable request retry + final Backoff retryBackoff = Backoff.of("exponential=200:10000,jitter=0.2,maxAttempts=5"); + final RetryRule retryRule = RetryRule.builder() + .onStatus(HttpStatus.SERVICE_UNAVAILABLE) + .onUnprocessed() + .thenBackoff(retryBackoff); + + return new ArmeriaHttpClient(config.withRetry(RetryingClient.newDecorator(retryRule))); + } + + // No-Op DNS resolver to avoid resolution issues in the unit test + private static class MockAddressResolverGroup extends AddressResolverGroup { + + @Override + protected AddressResolver newResolver(EventExecutor executor) { + return new MockAddressResolver(executor); + } + } + + private static class MockAddressResolver extends AbstractAddressResolver { + + private MockAddressResolver(EventExecutor executor) { + super(executor); + } + + @Override + protected boolean doIsResolved(InetSocketAddress address) { + return !address.isUnresolved(); + } + + private InetSocketAddress resolveToLoopback(InetSocketAddress unresolvedAddress) { + return new InetSocketAddress(InetAddress.getLoopbackAddress(), unresolvedAddress.getPort()); + } + + @Override + protected void doResolve(InetSocketAddress unresolvedAddress, Promise promise) { + promise.setSuccess(resolveToLoopback(unresolvedAddress)); + } + + @Override + protected void doResolveAll(InetSocketAddress unresolvedAddress, Promise> promise) { + promise.setSuccess(Collections.singletonList(resolveToLoopback(unresolvedAddress))); + } + } +} diff --git a/scribejava-httpclient-ning/pom.xml b/scribejava-httpclient-ning/pom.xml index 1bd473637..39696d698 100644 --- a/scribejava-httpclient-ning/pom.xml +++ b/scribejava-httpclient-ning/pom.xml @@ -5,10 +5,10 @@ com.github.scribejava scribejava - 4.1.3-SNAPSHOT + 8.3.4-SNAPSHOT ../pom.xml - + com.github.scribejava scribejava-httpclient-ning ScribeJava Ning Async Http Client support @@ -20,11 +20,24 @@ scribejava-core ${project.version} + + org.slf4j + slf4j-simple + 2.0.3 + test + com.ning async-http-client 1.9.40 + + com.github.scribejava + scribejava-core + ${project.version} + test-jar + test + 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 bcc843e00..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 @@ -1,6 +1,7 @@ package com.github.scribejava.httpclient.ning; import com.github.scribejava.core.httpclient.AbstractAsyncOnlyHttpClient; +import com.github.scribejava.core.httpclient.multipart.MultipartPayload; import com.github.scribejava.core.model.OAuthAsyncRequestCallback; import com.github.scribejava.core.model.OAuthConstants; import com.github.scribejava.core.model.OAuthRequest; @@ -17,6 +18,10 @@ public class NingHttpClient extends AbstractAsyncOnlyHttpClient { private final AsyncHttpClient client; + public NingHttpClient() { + this(NingHttpClientConfig.defaultConfig()); + } + public NingHttpClient(NingHttpClientConfig ningConfig) { final String ningAsyncHttpProviderClassName = ningConfig.getNingAsyncHttpProviderClassName(); AsyncHttpClientConfig config = ningConfig.getConfig(); @@ -41,7 +46,8 @@ public void close() { @Override public Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, - byte[] bodyContents, OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter) { + final byte[] bodyContents, OAuthAsyncRequestCallback callback, + OAuthRequest.ResponseConverter converter) { return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, BodySetter.BYTE_ARRAY, bodyContents, callback, converter); @@ -49,7 +55,16 @@ 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) { + MultipartPayload bodyContents, OAuthAsyncRequestCallback callback, + OAuthRequest.ResponseConverter converter) { + + throw new UnsupportedOperationException("NingHttpClient does not support MultipartPayload yet."); + } + + @Override + public Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, + final String bodyContents, OAuthAsyncRequestCallback callback, + OAuthRequest.ResponseConverter converter) { return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, BodySetter.STRING, bodyContents, callback, converter); @@ -57,7 +72,8 @@ public Future executeAsync(String userAgent, Map headers, @Override public Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, - File bodyContents, OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter) { + final File bodyContents, OAuthAsyncRequestCallback callback, + OAuthRequest.ResponseConverter converter) { return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, BodySetter.FILE, bodyContents, callback, converter); @@ -66,7 +82,7 @@ public Future executeAsync(String userAgent, Map headers, private Future doExecuteAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, BodySetter bodySetter, Object bodyContents, OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter) { - AsyncHttpClient.BoundRequestBuilder boundRequestBuilder; + final AsyncHttpClient.BoundRequestBuilder boundRequestBuilder; switch (httpVerb) { case GET: boundRequestBuilder = client.prepareGet(completeUrl); @@ -86,14 +102,15 @@ private Future doExecuteAsync(String userAgent, Map heade if (httpVerb.isPermitBody()) { if (!headers.containsKey(CONTENT_TYPE)) { - boundRequestBuilder = boundRequestBuilder.addHeader(CONTENT_TYPE, DEFAULT_CONTENT_TYPE); + boundRequestBuilder.addHeader(CONTENT_TYPE, DEFAULT_CONTENT_TYPE); } - boundRequestBuilder = bodySetter.setBody(boundRequestBuilder, bodyContents); + bodySetter.setBody(boundRequestBuilder, bodyContents); } for (Map.Entry header : headers.entrySet()) { boundRequestBuilder.addHeader(header.getKey(), header.getValue()); } + if (userAgent != null) { boundRequestBuilder.setHeader(OAuthConstants.USER_AGENT_HEADER_NAME, userAgent); } diff --git a/scribejava-httpclient-ning/src/main/java/com/github/scribejava/httpclient/ning/NingHttpClientConfig.java b/scribejava-httpclient-ning/src/main/java/com/github/scribejava/httpclient/ning/NingHttpClientConfig.java index 9eb5d83c2..6e8842cc7 100644 --- a/scribejava-httpclient-ning/src/main/java/com/github/scribejava/httpclient/ning/NingHttpClientConfig.java +++ b/scribejava-httpclient-ning/src/main/java/com/github/scribejava/httpclient/ning/NingHttpClientConfig.java @@ -20,6 +20,11 @@ public void setNingAsyncHttpProviderClassName(String ningAsyncHttpProviderClassN this.ningAsyncHttpProviderClassName = ningAsyncHttpProviderClassName; } + public NingHttpClientConfig withNingAsyncHttpProviderClassName(String ningAsyncHttpProviderClassName) { + this.ningAsyncHttpProviderClassName = ningAsyncHttpProviderClassName; + return this; + } + public AsyncHttpClientConfig getConfig() { return config; } diff --git a/scribejava-httpclient-ning/src/main/java/com/github/scribejava/httpclient/ning/OAuthAsyncCompletionHandler.java b/scribejava-httpclient-ning/src/main/java/com/github/scribejava/httpclient/ning/OAuthAsyncCompletionHandler.java index 5ce461b1b..cd8a25ce1 100644 --- a/scribejava-httpclient-ning/src/main/java/com/github/scribejava/httpclient/ning/OAuthAsyncCompletionHandler.java +++ b/scribejava-httpclient-ning/src/main/java/com/github/scribejava/httpclient/ning/OAuthAsyncCompletionHandler.java @@ -4,7 +4,6 @@ import com.github.scribejava.core.model.OAuthRequest; import com.github.scribejava.core.model.Response; import com.ning.http.client.AsyncCompletionHandler; -import com.ning.http.client.FluentCaseInsensitiveStringsMap; import java.io.IOException; import java.util.HashMap; import java.util.List; @@ -22,25 +21,31 @@ public OAuthAsyncCompletionHandler(OAuthAsyncRequestCallback callback, } @Override - public T onCompleted(com.ning.http.client.Response ningResponse) throws IOException { - final FluentCaseInsensitiveStringsMap map = ningResponse.getHeaders(); - final Map headersMap = new HashMap<>(); - for (FluentCaseInsensitiveStringsMap.Entry> header : map) { - final StringBuilder value = new StringBuilder(); - for (String str : header.getValue()) { - value.append(str); + public T onCompleted(com.ning.http.client.Response ningResponse) { + try { + final Map headersMap = new HashMap<>(); + + for (Map.Entry> header : ningResponse.getHeaders().entrySet()) { + final StringBuilder value = new StringBuilder(); + for (String str : header.getValue()) { + value.append(str); + } + headersMap.put(header.getKey(), value.toString()); } - headersMap.put(header.getKey(), value.toString()); - } - final Response response = new Response(ningResponse.getStatusCode(), ningResponse.getStatusText(), headersMap, - ningResponse.getResponseBodyAsStream()); - @SuppressWarnings("unchecked") - final T t = converter == null ? (T) response : converter.convert(response); - if (callback != null) { - callback.onCompleted(t); + final Response response = new Response(ningResponse.getStatusCode(), ningResponse.getStatusText(), + headersMap, ningResponse.getResponseBodyAsStream()); + + @SuppressWarnings("unchecked") + final T t = converter == null ? (T) response : converter.convert(response); + if (callback != null) { + callback.onCompleted(t); + } + return t; + } catch (IOException | RuntimeException e) { + onThrowable(e); + return null; } - return t; } @Override @@ -49,4 +54,4 @@ public void onThrowable(Throwable t) { callback.onThrowable(t); } } -}; +} diff --git a/scribejava-httpclient-ning/src/test/java/com/github/scribejava/httpclient/ning/MockResponse.java b/scribejava-httpclient-ning/src/test/java/com/github/scribejava/httpclient/ning/MockResponse.java new file mode 100644 index 000000000..af75bf871 --- /dev/null +++ b/scribejava-httpclient-ning/src/test/java/com/github/scribejava/httpclient/ning/MockResponse.java @@ -0,0 +1,122 @@ +package com.github.scribejava.httpclient.ning; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.List; + +import com.ning.http.client.FluentCaseInsensitiveStringsMap; +import com.ning.http.client.Response; +import com.ning.http.client.cookie.Cookie; +import com.ning.http.client.uri.Uri; + +public class MockResponse implements Response { + + private final int statusCode; + private final String statusText; + private final FluentCaseInsensitiveStringsMap headers; + private final byte[] body; + + public MockResponse(int statusCode, String statusText, FluentCaseInsensitiveStringsMap headers, byte[] body) { + this.statusCode = statusCode; + this.statusText = statusText; + this.headers = headers; + this.body = body; + } + + @Override + public int getStatusCode() { + return statusCode; + } + + @Override + public String getStatusText() { + return statusText; + } + + @Override + public byte[] getResponseBodyAsBytes() throws IOException { + return body; + } + + @Override + public ByteBuffer getResponseBodyAsByteBuffer() throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public InputStream getResponseBodyAsStream() throws IOException { + return new ByteArrayInputStream(getResponseBodyAsBytes()); + } + + @Override + public String getResponseBodyExcerpt(final int maxLength, final String charset) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public String getResponseBodyExcerpt(final int maxLength) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public String getResponseBody(final String charset) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public String getResponseBody() throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public Uri getUri() { + throw new UnsupportedOperationException(); + } + + @Override + public String getContentType() { + throw new UnsupportedOperationException(); + } + + @Override + public String getHeader(final String name) { + return headers.getFirstValue(name); + } + + @Override + public List getHeaders(final String name) { + return headers.get(name); + } + + @Override + public FluentCaseInsensitiveStringsMap getHeaders() { + return headers; + } + + @Override + public boolean isRedirected() { + throw new UnsupportedOperationException(); + } + + @Override + public List getCookies() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean hasResponseStatus() { + return true; + } + + @Override + public boolean hasResponseHeaders() { + return !this.headers.isEmpty(); + } + + @Override + public boolean hasResponseBody() { + return body != null && body.length > 0; + } +} diff --git a/scribejava-httpclient-ning/src/test/java/com/github/scribejava/httpclient/ning/NingHttpClientTest.java b/scribejava-httpclient-ning/src/test/java/com/github/scribejava/httpclient/ning/NingHttpClientTest.java new file mode 100644 index 000000000..6ebf6456f --- /dev/null +++ b/scribejava-httpclient-ning/src/test/java/com/github/scribejava/httpclient/ning/NingHttpClientTest.java @@ -0,0 +1,12 @@ +package com.github.scribejava.httpclient.ning; + +import com.github.scribejava.core.AbstractClientTest; +import com.github.scribejava.core.httpclient.HttpClient; + +public class NingHttpClientTest extends AbstractClientTest { + + @Override + protected HttpClient createNewClient() { + return new NingHttpClient(); + } +} diff --git a/scribejava-httpclient-ning/src/test/java/com/github/scribejava/httpclient/ning/OAuthAsyncCompletionHandlerTest.java b/scribejava-httpclient-ning/src/test/java/com/github/scribejava/httpclient/ning/OAuthAsyncCompletionHandlerTest.java new file mode 100644 index 000000000..7bc56e1e1 --- /dev/null +++ b/scribejava-httpclient-ning/src/test/java/com/github/scribejava/httpclient/ning/OAuthAsyncCompletionHandlerTest.java @@ -0,0 +1,100 @@ +package com.github.scribejava.httpclient.ning; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; + +import org.junit.Before; +import org.junit.Test; + +import com.github.scribejava.core.exceptions.OAuthException; +import com.github.scribejava.core.model.OAuthAsyncRequestCallback; +import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.model.Response; +import com.ning.http.client.FluentCaseInsensitiveStringsMap; + +public class OAuthAsyncCompletionHandlerTest { + + private static final AllGoodResponseConverter ALL_GOOD_RESPONSE_CONVERTER = new AllGoodResponseConverter(); + private static final OAuthExceptionResponseConverter OAUTH_EXCEPTION_RESPONSE_CONVERTER + = new OAuthExceptionResponseConverter(); + + private OAuthAsyncCompletionHandler handler; + private TestCallback callback; + + private static class TestCallback implements OAuthAsyncRequestCallback { + + private Throwable throwable; + private String response; + + @Override + public void onCompleted(String response) { + this.response = response; + } + + @Override + public void onThrowable(Throwable throwable) { + this.throwable = throwable; + } + + public Throwable getThrowable() { + return throwable; + } + + public String getResponse() { + return response; + } + + } + + @Before + public void setUp() { + callback = new TestCallback(); + } + + @Test + public void shouldReleaseLatchOnSuccess() throws Exception { + handler = new OAuthAsyncCompletionHandler<>(callback, ALL_GOOD_RESPONSE_CONVERTER); + + final com.ning.http.client.Response response + = new MockResponse(200, "ok", new FluentCaseInsensitiveStringsMap(), new byte[0]); + handler.onCompleted(response); + assertNotNull(callback.getResponse()); + assertNull(callback.getThrowable()); + // verify latch is released + assertEquals("All good", callback.getResponse()); + } + + @Test + public void shouldReportOAuthException() throws Exception { + handler = new OAuthAsyncCompletionHandler<>(callback, OAUTH_EXCEPTION_RESPONSE_CONVERTER); + + final com.ning.http.client.Response response + = new MockResponse(200, "ok", new FluentCaseInsensitiveStringsMap(), new byte[0]); + handler.onCompleted(response); + assertNull(callback.getResponse()); + assertNotNull(callback.getThrowable()); + assertTrue(callback.getThrowable() instanceof OAuthException); + } + + private static class AllGoodResponseConverter implements OAuthRequest.ResponseConverter { + + @Override + public String convert(Response response) throws IOException { + response.close(); + return "All good"; + } + } + + private static class OAuthExceptionResponseConverter implements OAuthRequest.ResponseConverter { + + @Override + public String convert(Response response) throws IOException { + response.close(); + throw new OAuthException("bad oauth"); + } + } +} diff --git a/scribejava-httpclient-okhttp/pom.xml b/scribejava-httpclient-okhttp/pom.xml index 2e122949c..59bc2bffc 100644 --- a/scribejava-httpclient-okhttp/pom.xml +++ b/scribejava-httpclient-okhttp/pom.xml @@ -5,10 +5,10 @@ com.github.scribejava scribejava - 4.1.3-SNAPSHOT + 8.3.4-SNAPSHOT ../pom.xml - + com.github.scribejava scribejava-httpclient-okhttp ScribeJava Async OkHttp Client support @@ -23,12 +23,13 @@ com.squareup.okhttp3 okhttp - 3.8.1 + 4.10.0 - com.squareup.okhttp3 - mockwebserver - 3.8.1 + com.github.scribejava + scribejava-core + ${project.version} + test-jar test diff --git a/scribejava-httpclient-okhttp/src/main/java/com/github/scribejava/httpclient/okhttp/OAuthAsyncCompletionHandler.java b/scribejava-httpclient-okhttp/src/main/java/com/github/scribejava/httpclient/okhttp/OAuthAsyncCompletionHandler.java index af59abbb5..787b70395 100644 --- a/scribejava-httpclient-okhttp/src/main/java/com/github/scribejava/httpclient/okhttp/OAuthAsyncCompletionHandler.java +++ b/scribejava-httpclient-okhttp/src/main/java/com/github/scribejava/httpclient/okhttp/OAuthAsyncCompletionHandler.java @@ -22,10 +22,11 @@ class OAuthAsyncCompletionHandler implements Callback { } @Override - public void onFailure(Call call, IOException e) { + public void onFailure(Call call, IOException exception) { try { + okHttpFuture.setException(exception); if (callback != null) { - callback.onThrowable(e); + callback.onThrowable(exception); } } finally { okHttpFuture.finish(); @@ -33,16 +34,22 @@ public void onFailure(Call call, IOException e) { } @Override - public void onResponse(Call call, okhttp3.Response okHttpResponse) throws IOException { + public void onResponse(Call call, okhttp3.Response okHttpResponse) { try { final Response response = OkHttpHttpClient.convertResponse(okHttpResponse); - - @SuppressWarnings("unchecked") - final T t = converter == null ? (T) response : converter.convert(response); - okHttpFuture.setResult(t); - if (callback != null) { - callback.onCompleted(t); + try { + @SuppressWarnings("unchecked") + final T t = converter == null ? (T) response : converter.convert(response); + okHttpFuture.setResult(t); + if (callback != null) { + callback.onCompleted(t); + } + } catch (IOException | RuntimeException e) { + okHttpFuture.setException(e); + if (callback != null) { + callback.onThrowable(e); + } } } finally { okHttpFuture.finish(); diff --git a/scribejava-httpclient-okhttp/src/main/java/com/github/scribejava/httpclient/okhttp/OkHttpFuture.java b/scribejava-httpclient-okhttp/src/main/java/com/github/scribejava/httpclient/okhttp/OkHttpFuture.java index ebf459260..417b03e0a 100644 --- a/scribejava-httpclient-okhttp/src/main/java/com/github/scribejava/httpclient/okhttp/OkHttpFuture.java +++ b/scribejava-httpclient-okhttp/src/main/java/com/github/scribejava/httpclient/okhttp/OkHttpFuture.java @@ -5,6 +5,7 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; + import okhttp3.Call; public class OkHttpFuture implements Future { @@ -12,6 +13,7 @@ public class OkHttpFuture implements Future { private final CountDownLatch latch = new CountDownLatch(1); private final Call call; private T result; + private Exception exception; public OkHttpFuture(Call call) { this.call = call; @@ -33,19 +35,28 @@ public boolean isDone() { return call.isExecuted(); } + public void setException(Exception exception) { + this.exception = exception; + } + @Override public T get() throws InterruptedException, ExecutionException { latch.await(); + if (exception != null) { + throw new ExecutionException(exception); + } return result; } @Override public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if (latch.await(timeout, unit)) { + if (exception != null) { + throw new ExecutionException(exception); + } return result; - } else { - throw new TimeoutException(); } + throw new TimeoutException(); } void finish() { diff --git a/scribejava-httpclient-okhttp/src/main/java/com/github/scribejava/httpclient/okhttp/OkHttpHttpClient.java b/scribejava-httpclient-okhttp/src/main/java/com/github/scribejava/httpclient/okhttp/OkHttpHttpClient.java index 66d2daee0..f82697db0 100644 --- a/scribejava-httpclient-okhttp/src/main/java/com/github/scribejava/httpclient/okhttp/OkHttpHttpClient.java +++ b/scribejava-httpclient-okhttp/src/main/java/com/github/scribejava/httpclient/okhttp/OkHttpHttpClient.java @@ -1,6 +1,7 @@ package com.github.scribejava.httpclient.okhttp; import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.multipart.MultipartPayload; import com.github.scribejava.core.model.OAuthAsyncRequestCallback; import com.github.scribejava.core.model.OAuthConstants; import com.github.scribejava.core.model.OAuthRequest; @@ -11,13 +12,12 @@ import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.internal.http.HttpMethod; - import java.io.IOException; import java.util.Map; import java.util.concurrent.Future; - import com.github.scribejava.core.model.Response; import java.io.File; +import java.io.InputStream; import java.util.HashMap; import java.util.concurrent.ExecutionException; import okhttp3.Cache; @@ -30,6 +30,10 @@ public class OkHttpHttpClient implements HttpClient { private final OkHttpClient client; + public OkHttpHttpClient() { + this(OkHttpHttpClientConfig.defaultConfig()); + } + public OkHttpHttpClient(OkHttpHttpClientConfig config) { final OkHttpClient.Builder clientBuilder = config.getClientBuilder(); client = clientBuilder == null ? new OkHttpClient() : clientBuilder.build(); @@ -52,13 +56,23 @@ 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, BodyType.BYTE_ARRAY, bodyContents, callback, converter); } + @Override + public Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, + MultipartPayload bodyContents, OAuthAsyncRequestCallback callback, + OAuthRequest.ResponseConverter converter) { + + throw new UnsupportedOperationException("OKHttpClient does not support Multipart payload for the moment"); + } + @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, BodyType.STRING, bodyContents, callback, converter); } @@ -66,6 +80,7 @@ public Future executeAsync(String userAgent, Map headers, @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, BodyType.FILE, bodyContents, callback, converter); } @@ -82,18 +97,28 @@ private Future doExecuteAsync(String userAgent, Map heade @Override public Response execute(String userAgent, Map headers, Verb httpVerb, String completeUrl, byte[] bodyContents) throws InterruptedException, ExecutionException, IOException { + return doExecute(userAgent, headers, httpVerb, completeUrl, BodyType.BYTE_ARRAY, bodyContents); } + @Override + public Response execute(String userAgent, Map headers, Verb httpVerb, String completeUrl, + MultipartPayload bodyContents) throws InterruptedException, ExecutionException, IOException { + + throw new UnsupportedOperationException("OKHttpClient does not support Multipart payload for the moment"); + } + @Override public Response execute(String userAgent, Map headers, Verb httpVerb, String completeUrl, String bodyContents) throws InterruptedException, ExecutionException, IOException { + return doExecute(userAgent, headers, httpVerb, completeUrl, BodyType.STRING, bodyContents); } @Override public Response execute(String userAgent, Map headers, Verb httpVerb, String completeUrl, File bodyContents) throws InterruptedException, ExecutionException, IOException { + return doExecute(userAgent, headers, httpVerb, completeUrl, BodyType.FILE, bodyContents); } @@ -128,6 +153,7 @@ private Call createCall(String userAgent, Map headers, Verb http for (Map.Entry header : headers.entrySet()) { requestBuilder.addHeader(header.getKey(), header.getValue()); } + if (userAgent != null) { requestBuilder.header(OAuthConstants.USER_AGENT_HEADER_NAME, userAgent); } @@ -140,19 +166,19 @@ private enum BodyType { BYTE_ARRAY { @Override RequestBody createBody(MediaType mediaType, Object bodyContents) { - return RequestBody.create(mediaType, (byte[]) bodyContents); + return RequestBody.create((byte[]) bodyContents, mediaType); } }, STRING { @Override RequestBody createBody(MediaType mediaType, Object bodyContents) { - return RequestBody.create(mediaType, (String) bodyContents); + return RequestBody.create((String) bodyContents, mediaType); } }, FILE { @Override RequestBody createBody(MediaType mediaType, Object bodyContents) { - return RequestBody.create(mediaType, (File) bodyContents); + return RequestBody.create((File) bodyContents, mediaType); } }; @@ -162,14 +188,14 @@ RequestBody createBody(MediaType mediaType, Object bodyContents) { static Response convertResponse(okhttp3.Response okHttpResponse) { final Headers headers = okHttpResponse.headers(); final Map headersMap = new HashMap<>(); - - for (String name : headers.names()) { - headersMap.put(name, headers.get(name)); + for (String headerName : headers.names()) { + headersMap.put(headerName, headers.get(headerName)); } final ResponseBody body = okHttpResponse.body(); - return new Response(okHttpResponse.code(), okHttpResponse.message(), headersMap, - body == null ? null : body.byteStream()); - + final InputStream bodyStream = body == null ? null : body.byteStream(); + return new Response(okHttpResponse.code(), okHttpResponse.message(), headersMap, bodyStream, bodyStream, body, + okHttpResponse); } + } diff --git a/scribejava-httpclient-okhttp/src/test/java/com/github/scribejava/httpclient/okhttp/MockCall.java b/scribejava-httpclient-okhttp/src/test/java/com/github/scribejava/httpclient/okhttp/MockCall.java new file mode 100644 index 000000000..7fa14a630 --- /dev/null +++ b/scribejava-httpclient-okhttp/src/test/java/com/github/scribejava/httpclient/okhttp/MockCall.java @@ -0,0 +1,60 @@ +package com.github.scribejava.httpclient.okhttp; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; + +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.Request; +import okhttp3.Response; +import okio.Timeout; + +public class MockCall implements Call { + + private final Collection callbacks = new ArrayList<>(); + private boolean canceled; + + @Override + public void enqueue(Callback responseCallback) { + callbacks.add(responseCallback); + } + + @Override + public void cancel() { + canceled = true; + for (Callback callback : callbacks) { + callback.onFailure(this, new IOException("Canceled")); + } + } + + @Override + public boolean isCanceled() { + return canceled; + } + + @Override + public Request request() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Response execute() throws IOException { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean isExecuted() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public MockCall clone() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Timeout timeout() { + throw new UnsupportedOperationException("Not supported yet."); + } +} 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 new file mode 100644 index 000000000..b58e8df93 --- /dev/null +++ b/scribejava-httpclient-okhttp/src/test/java/com/github/scribejava/httpclient/okhttp/OAuthAsyncCompletionHandlerTest.java @@ -0,0 +1,205 @@ +package com.github.scribejava.httpclient.okhttp; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertThrows; + +import java.io.IOException; +import java.util.concurrent.ExecutionException; + +import org.junit.Before; +import org.junit.Test; + +import com.github.scribejava.core.exceptions.OAuthException; +import com.github.scribejava.core.model.OAuthAsyncRequestCallback; +import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.model.Response; + +import okhttp3.Call; +import okhttp3.MediaType; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.ResponseBody; +import org.junit.function.ThrowingRunnable; + +public class OAuthAsyncCompletionHandlerTest { + + private static final AllGoodResponseConverter ALL_GOOD_RESPONSE_CONVERTER = new AllGoodResponseConverter(); + private static final ExceptionResponseConverter EXCEPTION_RESPONSE_CONVERTER = new ExceptionResponseConverter(); + private static final OAuthExceptionResponseConverter OAUTH_EXCEPTION_RESPONSE_CONVERTER + = new OAuthExceptionResponseConverter(); + + private OAuthAsyncCompletionHandler handler; + private Call call; + private OkHttpFuture future; + private TestCallback callback; + + private static class TestCallback implements OAuthAsyncRequestCallback { + + private Throwable throwable; + private String response; + + @Override + public void onCompleted(String response) { + this.response = response; + } + + @Override + public void onThrowable(Throwable throwable) { + this.throwable = throwable; + } + + public Throwable getThrowable() { + return throwable; + } + + public String getResponse() { + return response; + } + + } + + @Before + public void setUp() { + callback = new TestCallback(); + call = new MockCall(); + future = new OkHttpFuture<>(call); + } + + @Test + public void shouldReleaseLatchOnSuccess() throws Exception { + handler = new OAuthAsyncCompletionHandler<>(callback, ALL_GOOD_RESPONSE_CONVERTER, future); + call.enqueue(handler); + + final Request request = new Request.Builder().url("http://localhost/").build(); + final okhttp3.Response response = new okhttp3.Response.Builder() + .request(request) + .protocol(Protocol.HTTP_1_1) + .code(200) + .message("ok") + .body(ResponseBody.create(new byte[0], MediaType.get("text/plain"))) + .build(); + handler.onResponse(call, response); + assertNotNull(callback.getResponse()); + assertNull(callback.getThrowable()); + // verify latch is released + assertEquals("All good", future.get()); + } + + @Test + public void shouldReleaseLatchOnIOException() { + handler = new OAuthAsyncCompletionHandler<>(callback, EXCEPTION_RESPONSE_CONVERTER, future); + call.enqueue(handler); + + final Request request = new Request.Builder().url("http://localhost/").build(); + final okhttp3.Response response = new okhttp3.Response.Builder() + .request(request) + .protocol(Protocol.HTTP_1_1) + .code(200) + .message("ok") + .body(ResponseBody.create(new byte[0], MediaType.get("text/plain"))) + .build(); + handler.onResponse(call, response); + assertNull(callback.getResponse()); + assertNotNull(callback.getThrowable()); + assertTrue(callback.getThrowable() instanceof IOException); + // verify latch is released + assertThrows(ExecutionException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + future.get(); + } + }); + } + + @Test + public void shouldReportOAuthException() { + handler = new OAuthAsyncCompletionHandler<>(callback, OAUTH_EXCEPTION_RESPONSE_CONVERTER, future); + call.enqueue(handler); + + final Request request = new Request.Builder().url("http://localhost/").build(); + final okhttp3.Response response = new okhttp3.Response.Builder() + .request(request) + .protocol(Protocol.HTTP_1_1) + .code(200) + .message("ok") + .body(ResponseBody.create(new byte[0], MediaType.get("text/plain"))) + .build(); + handler.onResponse(call, response); + assertNull(callback.getResponse()); + assertNotNull(callback.getThrowable()); + assertTrue(callback.getThrowable() instanceof OAuthException); + // verify latch is released + assertThrows(ExecutionException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + future.get(); + } + }); + } + + @Test + public void shouldReleaseLatchOnCancel() { + handler = new OAuthAsyncCompletionHandler<>(callback, ALL_GOOD_RESPONSE_CONVERTER, future); + call.enqueue(handler); + + future.cancel(true); + assertNull(callback.getResponse()); + assertNotNull(callback.getThrowable()); + assertTrue(callback.getThrowable() instanceof IOException); + // verify latch is released + assertThrows(ExecutionException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + future.get(); + } + }); + } + + @Test + public void shouldReleaseLatchOnFailure() { + handler = new OAuthAsyncCompletionHandler<>(callback, ALL_GOOD_RESPONSE_CONVERTER, future); + call.enqueue(handler); + + handler.onFailure(call, new IOException()); + assertNull(callback.getResponse()); + assertNotNull(callback.getThrowable()); + assertTrue(callback.getThrowable() instanceof IOException); + // verify latch is released + assertThrows(ExecutionException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + future.get(); + } + }); + } + + private static class AllGoodResponseConverter implements OAuthRequest.ResponseConverter { + + @Override + public String convert(Response response) throws IOException { + response.close(); + return "All good"; + } + } + + private static class ExceptionResponseConverter implements OAuthRequest.ResponseConverter { + + @Override + public String convert(Response response) throws IOException { + response.close(); + throw new IOException("Failed to convert"); + } + } + + private static class OAuthExceptionResponseConverter implements OAuthRequest.ResponseConverter { + + @Override + public String convert(Response response) throws IOException { + response.close(); + throw new OAuthException("bad oauth"); + } + } +} diff --git a/scribejava-httpclient-okhttp/src/test/java/com/github/scribejava/httpclient/okhttp/OkHttpHttpClientTest.java b/scribejava-httpclient-okhttp/src/test/java/com/github/scribejava/httpclient/okhttp/OkHttpHttpClientTest.java index 496882cdd..80fdf2834 100644 --- a/scribejava-httpclient-okhttp/src/test/java/com/github/scribejava/httpclient/okhttp/OkHttpHttpClientTest.java +++ b/scribejava-httpclient-okhttp/src/test/java/com/github/scribejava/httpclient/okhttp/OkHttpHttpClientTest.java @@ -1,112 +1,12 @@ package com.github.scribejava.httpclient.okhttp; +import com.github.scribejava.core.AbstractClientTest; import com.github.scribejava.core.httpclient.HttpClient; -import com.github.scribejava.core.model.OAuthConfig; -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 com.github.scribejava.core.oauth.OAuthService; -import com.github.scribejava.core.utils.StreamUtils; -import okhttp3.HttpUrl; -import okhttp3.OkHttpClient; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import okhttp3.mockwebserver.RecordedRequest; -import org.junit.Before; -import org.junit.Test; -import java.util.concurrent.TimeUnit; +public class OkHttpHttpClientTest extends AbstractClientTest { -import static org.junit.Assert.assertEquals; - -public class OkHttpHttpClientTest { - private OAuthService oAuthService; - - @Before - public void setUp() { - final HttpClient client = new OkHttpHttpClient(new OkHttpClient()); - oAuthService = new OAuth20Service(null, - new OAuthConfig("test", "test", null, null, null, null, null, null, null, client)); - } - - - @Test - public void shouldSendGetRequest() throws Exception { - final String expectedResponseBody = "response body"; - - final MockWebServer server = new MockWebServer(); - server.enqueue(new MockResponse().setBody(expectedResponseBody)); - server.start(); - - final HttpUrl baseUrl = server.url("/testUrl"); - - final OAuthRequest request = new OAuthRequest(Verb.GET, baseUrl.toString()); - final Response response = oAuthService.execute(request, null).get(30, TimeUnit.SECONDS); - - assertEquals(expectedResponseBody, response.getBody()); - - final RecordedRequest recordedRequest = server.takeRequest(); - assertEquals("GET", recordedRequest.getMethod()); - - server.shutdown(); - } - - @Test - public void shouldSendPostRequest() throws Exception { - final String expectedResponseBody = "response body"; - final String expectedRequestBody = "request body"; - - final MockWebServer server = new MockWebServer(); - server.enqueue(new MockResponse().setBody(expectedResponseBody)); - server.enqueue(new MockResponse().setBody(expectedResponseBody)); - server.start(); - - final HttpUrl baseUrl = server.url("/testUrl"); - - // request with body - OAuthRequest request = new OAuthRequest(Verb.POST, baseUrl.toString()); - request.setPayload(expectedRequestBody); - Response response = oAuthService.execute(request, null).get(30, TimeUnit.SECONDS); - - assertEquals(expectedResponseBody, response.getBody()); - - RecordedRequest recordedRequest = server.takeRequest(); - assertEquals("POST", recordedRequest.getMethod()); - assertEquals(expectedRequestBody, recordedRequest.getBody().readUtf8()); - - - // request with empty body - request = new OAuthRequest(Verb.POST, baseUrl.toString()); - response = oAuthService.execute(request, null).get(30, TimeUnit.SECONDS); - - assertEquals(expectedResponseBody, response.getBody()); - - recordedRequest = server.takeRequest(); - assertEquals("POST", recordedRequest.getMethod()); - assertEquals("", recordedRequest.getBody().readUtf8()); - - server.shutdown(); - } - - @Test - public void shouldReadResponseStream() throws Exception { - final String expectedResponseBody = "response body"; - - final MockWebServer server = new MockWebServer(); - server.enqueue(new MockResponse().setBody(expectedResponseBody)); - server.start(); - - final HttpUrl baseUrl = server.url("/testUrl"); - - final OAuthRequest request = new OAuthRequest(Verb.GET, baseUrl.toString()); - final Response response = oAuthService.execute(request, null).get(30, TimeUnit.SECONDS); - - assertEquals(expectedResponseBody, StreamUtils.getStreamContents(response.getStream())); - - final RecordedRequest recordedRequest = server.takeRequest(); - assertEquals("GET", recordedRequest.getMethod()); - - server.shutdown(); + @Override + protected HttpClient createNewClient() { + return new OkHttpHttpClient(); } } 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); + } + +}