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/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 993ff954a..b90ef2e5f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ -# Welcome to the home of ScribeJava, the simple OAuth Java lib! +# Welcome to the home of ScribeJava, the simple OAuth client Java lib! + +[![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? @@ -7,60 +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() - .apiKey(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 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. -### Async - -You can use ning async http client out-of-box, just use ServiceBuilderAsync - -### Supports all major 1.0a and 2.0 OAuth APIs out-of-the-box - -* AWeber (http://www.aweber.com/) -* Digg (http://digg.com/) -* Доктор на работе (http://www.doktornarabote.ru/) -* Facebook (https://www.facebook.com/) -* Flickr (https://www.flickr.com/) -* Foursquare (https://foursquare.com/) -* Freelancer (https://www.freelancer.com/) -* Genius (http://genius.com/) -* GitHub (https://github.com/) -* Google (https://www.google.ru/) -* 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/) -* 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/) -* 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/) +### 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) [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) + + just add corresponding maven modules to your pom + +### 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/) +* 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 @@ -86,7 +137,7 @@ You can pull ScribeJava from the central maven repository, just add these to you com.github.scribejava scribejava-apis - 2.8.1 + 8.3.3 ``` @@ -95,14 +146,29 @@ And in case you need just core classes (that's it, without any external API (FB, com.github.scribejava scribejava-core - 2.8.1 + 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 eee7f1cad..e962e073b 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,256 @@ +[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 + * upgrade ServiceBuilder to check apiKey preconditions compile-time (not run-time) + * 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) + * 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) + * 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) + * 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) + +[4.0.0] + * 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) + * introduce custom nonstandard Facebook AccessTokenErrorResponse + +[3.4.1] + * Drop deprecated methods + * Move doktornarabote.ru urls to https (thanks to https://github.com/ezibrov) + +[3.4.0] + * uncouple OAuthRequest and Service. OAuthRequest shouldn't know anything about OAuthservice. + You don't need OAuthService to create OAuthRequest anymore. Async request should be sent via OAuthService method. + * 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())') + * 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) + * move signRequest method to OAuthService (common for OAuth1 and OAuth2) (thanks to https://github.com/apomelov) + * drop deprecated setConnectionKeepAlive method + +[3.2.0] + * Add Naver API (thanks to chooco) + * 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/ + * 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) + [2.8.1] * add Salesforce sandbox API support @@ -37,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] @@ -80,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 68bf28a27..b484b6a71 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ com.github.scribejava scribejava pom - 2.8.2-SNAPSHOT + 8.3.4-SNAPSHOT ScribeJava OAuth Library The best OAuth library out there https://github.com/scribejava/scribejava @@ -16,7 +16,13 @@ scribejava-core + scribejava-java8 scribejava-apis + scribejava-httpclient-ahc + scribejava-httpclient-ning + scribejava-httpclient-okhttp + scribejava-httpclient-apache + scribejava-httpclient-armeria @@ -27,44 +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 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 @@ -73,37 +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.7 + com.squareup.okhttp3 + mockwebserver + 4.10.0 test - - commons-codec - commons-codec - 1.10 - compile - true - - - com.ning - async-http-client - 1.9.38 - provided - - - org.asynchttpclient - async-http-client - 2.0.9 - provided - @@ -111,7 +76,7 @@ org.apache.felix maven-bundle-plugin - 3.0.1 + 5.1.8 bundle-manifest @@ -125,29 +90,66 @@ org.apache.maven.plugins maven-jar-plugin - 3.0.2 + 3.3.0 ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.2.0 + + + com.puppycrawl.tools + checkstyle + 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.5.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 @@ -161,7 +163,7 @@ org.apache.maven.plugins maven-resources-plugin - 3.0.1 + 3.3.0 UTF-8 @@ -169,7 +171,7 @@ org.apache.maven.plugins maven-source-plugin - 3.0.1 + 3.2.1 attach-sources @@ -182,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 @@ -198,20 +203,14 @@ org.apache.maven.plugins maven-checkstyle-plugin - 2.17 - - - com.puppycrawl.tools - checkstyle - 7.0 - - validate validate - ${basedir}/src + + ${basedir}/src + checkstyle.xml UTF-8 true @@ -222,9 +221,58 @@ + + 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 + + release-sign-artifacts @@ -239,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 095ecbd2d..4e51bb8b5 100644 --- a/scribejava-apis/pom.xml +++ b/scribejava-apis/pom.xml @@ -5,10 +5,10 @@ com.github.scribejava scribejava - 2.8.2-SNAPSHOT + 8.3.4-SNAPSHOT ../pom.xml - + com.github.scribejava scribejava-apis ScribeJava APIs @@ -20,6 +20,42 @@ scribejava-core ${project.version} + + com.github.scribejava + scribejava-httpclient-ahc + ${project.version} + test + + + com.github.scribejava + scribejava-httpclient-ning + ${project.version} + test + + + com.github.scribejava + scribejava-httpclient-okhttp + ${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 new file mode 100644 index 000000000..91a4b7c16 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/BoxApi20.java @@ -0,0 +1,38 @@ +package com.github.scribejava.apis; + +import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignature; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignatureURIQueryParameter; + +/** + * Box.com Api + */ +public class BoxApi20 extends DefaultApi20 { + + + protected BoxApi20() { + } + + private static class InstanceHolder { + private static final BoxApi20 INSTANCE = new BoxApi20(); + } + + public static BoxApi20 instance() { + return InstanceHolder.INSTANCE; + } + + @Override + public String getAccessTokenEndpoint() { + return "https://api.box.com/oauth2/token"; + } + + @Override + protected String getAuthorizationBaseUrl() { + return "https://account.box.com/api/oauth2/authorize"; + } + + @Override + 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/DoktornaraboteApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/DoktornaraboteApi.java index 8965ea07f..1fcac7108 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/DoktornaraboteApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/DoktornaraboteApi.java @@ -1,9 +1,6 @@ package com.github.scribejava.apis; -import com.github.scribejava.apis.service.DoktornaraboteOAuthServiceImpl; import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.model.OAuthConfig; -import com.github.scribejava.core.oauth.OAuth20Service; public class DoktornaraboteApi extends DefaultApi20 { @@ -20,16 +17,11 @@ public static DoktornaraboteApi instance() { @Override public String getAccessTokenEndpoint() { - return "http://auth.doktornarabote.ru/OAuth/Token"; + return "https://auth.doktornarabote.ru/OAuth/Token"; } @Override protected String getAuthorizationBaseUrl() { - return "http://auth.doktornarabote.ru/OAuth/Authorize"; - } - - @Override - public OAuth20Service createService(OAuthConfig config) { - return new DoktornaraboteOAuthServiceImpl(this, config); + return "https://auth.doktornarabote.ru/OAuth/Authorize"; } } 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 9bf5abfc0..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,14 +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.6 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 { @@ -20,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; @@ -27,16 +45,34 @@ public Verb getAccessTokenVerb() { @Override public String getAccessTokenEndpoint() { - return "https://graph.facebook.com/v2.6/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.6/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 0e1db4ea7..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,10 +9,27 @@ */ public class FlickrApi extends DefaultApi10a { + private static final String AUTHORIZE_URL = "https://www.flickr.com/services/oauth/authorize"; + + public enum FlickrPerm { + READ, WRITE, DELETE + }; + + /** + * read, write, or delete (delete includes read/write) + */ + private final String permString; + protected FlickrApi() { + permString = null; + } + + protected FlickrApi(FlickrPerm perm) { + permString = perm.name().toLowerCase(); } private static class InstanceHolder { + private static final FlickrApi INSTANCE = new FlickrApi(); } @@ -21,6 +37,10 @@ public static FlickrApi instance() { return InstanceHolder.INSTANCE; } + public static FlickrApi instance(FlickrPerm perm) { + return perm == null ? instance() : new FlickrApi(perm); + } + /** * {@inheritDoc} */ @@ -29,12 +49,9 @@ public String getAccessTokenEndpoint() { return "https://www.flickr.com/services/oauth/access_token"; } - /** - * {@inheritDoc} - */ @Override - public String getAuthorizationUrl(OAuth1RequestToken requestToken) { - return "https://www.flickr.com/services/oauth/authorize?oauth_token=" + requestToken.getToken(); + 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 cb574618d..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 @@ -2,6 +2,8 @@ import com.github.scribejava.core.builder.api.DefaultApi20; 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 { @@ -30,4 +32,9 @@ public String getAccessTokenEndpoint() { protected String getAuthorizationBaseUrl() { return "https://foursquare.com/oauth2/authenticate"; } + + @Override + 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/FrappeApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/FrappeApi.java new file mode 100644 index 000000000..ec4cc61f4 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/FrappeApi.java @@ -0,0 +1,42 @@ +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; + +public class FrappeApi extends DefaultApi20 { + + private final String serverURL; + private final String accessTokenEndpoint; + private final String authorizationBaseUrl; + + protected FrappeApi(String serverURL) { + this.serverURL = serverURL; + this.accessTokenEndpoint = serverURL + "/api/method/frappe.integrations.oauth2.get_token"; + this.authorizationBaseUrl = serverURL + "/api/method/frappe.integrations.oauth2.authorize"; + } + + public static FrappeApi instance(String serverUrl) { + return new FrappeApi(serverUrl); + } + + public String getServerURL() { + return serverURL; + } + + @Override + public String getAccessTokenEndpoint() { + return accessTokenEndpoint; + } + + @Override + protected String getAuthorizationBaseUrl() { + return authorizationBaseUrl; + } + + @Override + public TokenExtractor getAccessTokenExtractor() { + return OpenIdJsonTokenExtractor.instance(); + } +} 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 52fe4cc1f..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 @@ -1,12 +1,12 @@ 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.builder.api.OAuth1SignatureType; 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() { } @@ -19,6 +19,11 @@ public static FreelancerApi instance() { return InstanceHolder.INSTANCE; } + @Override + public OAuth1SignatureType getSignatureType() { + return OAuth1SignatureType.QUERY_STRING; + } + @Override public String getAccessTokenEndpoint() { return "http://api.freelancer.com/RequestAccessToken/requestAccessToken.xml?"; @@ -40,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 { @@ -71,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/GeniusApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/GeniusApi.java index 14570697c..03eae859c 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/GeniusApi.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/GeniusApi.java @@ -1,9 +1,6 @@ package com.github.scribejava.apis; -import com.github.scribejava.apis.service.GeniusOAuthServiceImpl; import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.model.OAuthConfig; -import com.github.scribejava.core.oauth.OAuth20Service; public class GeniusApi extends DefaultApi20 { @@ -28,9 +25,4 @@ public String getAccessTokenEndpoint() { protected String getAuthorizationBaseUrl() { return "https://api.genius.com/oauth/authorize"; } - - @Override - public OAuth20Service createService(OAuthConfig config) { - return new GeniusOAuthServiceImpl(this, config); - } } 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 157753f6d..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.GoogleJsonTokenExtractor; +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 GoogleJsonTokenExtractor.instance(); + 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 acb239fa5..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,10 +1,8 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.model.OAuthConfig; - -import com.github.scribejava.apis.service.HHOAuthServiceImpl; -import com.github.scribejava.core.oauth.OAuth20Service; +import com.github.scribejava.core.oauth2.clientauthentication.ClientAuthentication; +import com.github.scribejava.core.oauth2.clientauthentication.RequestBodyAuthenticationScheme; public class HHApi extends DefaultApi20 { @@ -12,6 +10,7 @@ protected HHApi() { } private static class InstanceHolder { + private static final HHApi INSTANCE = new HHApi(); } @@ -30,7 +29,7 @@ protected String getAuthorizationBaseUrl() { } @Override - public OAuth20Service createService(OAuthConfig config) { - return new HHOAuthServiceImpl(this, config); + 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 cd8917fd5..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 @@ -2,6 +2,8 @@ import com.github.scribejava.core.builder.api.DefaultApi20; 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. @@ -33,4 +35,9 @@ public String getAccessTokenEndpoint() { protected String getAuthorizationBaseUrl() { return "http://api.kaixin001.com/oauth2/authorize"; } + + @Override + 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 2d876ec5f..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,29 +1,24 @@ 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; - public LinkedInApi() { + protected LinkedInApi() { scopesAsString = null; } - public LinkedInApi(String... scopes) { - if (scopes == null || scopes.length == 0) { - scopesAsString = null; - } else { - final StringBuilder builder = new StringBuilder(); - for (String scope : scopes) { - builder.append('+').append(scope); - } - scopesAsString = "?scope=" + builder.substring(1); + protected LinkedInApi(String... scopes) { + final StringBuilder builder = new StringBuilder(); + for (String scope : scopes) { + builder.append('+').append(scope); } + scopesAsString = "?scope=" + builder.substring(1); } private static class InstanceHolder { @@ -35,6 +30,10 @@ public static LinkedInApi instance() { return InstanceHolder.INSTANCE; } + public static LinkedInApi instance(String... scopes) { + return scopes == null || scopes.length == 0 ? instance() : new LinkedInApi(scopes); + } + @Override public String getAccessTokenEndpoint() { return "https://api.linkedin.com/uas/oauth/accessToken"; @@ -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 88dc30e7f..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,9 +1,8 @@ package com.github.scribejava.apis; -import com.github.scribejava.apis.service.LinkedIn20ServiceImpl; import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.model.OAuthConfig; -import com.github.scribejava.core.oauth.OAuth20Service; +import com.github.scribejava.core.oauth2.clientauthentication.ClientAuthentication; +import com.github.scribejava.core.oauth2.clientauthentication.RequestBodyAuthenticationScheme; public class LinkedInApi20 extends DefaultApi20 { @@ -29,7 +28,7 @@ protected String getAuthorizationBaseUrl() { } @Override - public OAuth20Service createService(OAuthConfig config) { - return new LinkedIn20ServiceImpl(this, config); + 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 ee6c6ba12..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.model.Verb; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignature; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignatureURIQueryParameter; public class LiveApi extends DefaultApi20 { @@ -16,11 +17,6 @@ public static LiveApi instance() { return InstanceHolder.INSTANCE; } - @Override - public Verb getAccessTokenVerb() { - return Verb.GET; - } - @Override public String getAccessTokenEndpoint() { return "https://login.live.com/oauth20_token.srf"; @@ -28,6 +24,11 @@ public String getAccessTokenEndpoint() { @Override protected String getAuthorizationBaseUrl() { - return "https://oauth.live.com/authorize"; + return "https://login.live.com/oauth20_authorize.srf"; + } + + @Override + 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 f6d2fc706..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,6 +1,8 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignature; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignatureURIQueryParameter; public class MisfitApi extends DefaultApi20 { @@ -25,4 +27,9 @@ public String getAccessTokenEndpoint() { protected String getAuthorizationBaseUrl() { return "https://api.misfitwearables.com/auth/dialog/authorize"; } + + @Override + 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 new file mode 100644 index 000000000..f60871735 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/NaverApi.java @@ -0,0 +1,33 @@ +package com.github.scribejava.apis; + +import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignature; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignatureURIQueryParameter; + +public class NaverApi extends DefaultApi20 { + protected NaverApi() { + } + + private static class InstanceHolder { + private static final NaverApi INSTANCE = new NaverApi(); + } + + public static NaverApi instance() { + return NaverApi.InstanceHolder.INSTANCE; + } + + @Override + public String getAccessTokenEndpoint() { + return "https://nid.naver.com/oauth2.0/token?grant_type=authorization_code"; + } + + @Override + protected String getAuthorizationBaseUrl() { + return "https://nid.naver.com/oauth2.0/authorize"; + } + + @Override + 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 3144a77e0..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,9 +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.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 { @@ -11,6 +17,7 @@ protected OdnoklassnikiApi() { } private static class InstanceHolder { + private static final OdnoklassnikiApi INSTANCE = new OdnoklassnikiApi(); } @@ -29,7 +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 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 0f44d7b12..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,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 PinterestApi extends DefaultApi20 { @@ -24,4 +26,9 @@ public String getAccessTokenEndpoint() { protected String getAuthorizationBaseUrl() { return "https://api.pinterest.com/oauth"; } + + @Override + 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 da10760c3..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 @@ -2,6 +2,8 @@ import com.github.scribejava.core.builder.api.DefaultApi20; 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. @@ -33,4 +35,9 @@ public String getAccessTokenEndpoint() { protected String getAuthorizationBaseUrl() { return "https://graph.renren.com/oauth/authorize"; } + + @Override + 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 1799806f1..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,6 +1,8 @@ package com.github.scribejava.apis; import com.github.scribejava.core.builder.api.DefaultApi20; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignature; +import com.github.scribejava.core.oauth2.bearersignature.BearerSignatureURIQueryParameter; /** * SinaWeibo OAuth 2.0 api. @@ -27,4 +29,9 @@ public String getAccessTokenEndpoint() { protected String getAuthorizationBaseUrl() { return "https://api.weibo.com/oauth2/authorize"; } + + @Override + 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 c6dfcb101..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 @@ -4,6 +4,8 @@ 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, @@ -36,4 +38,9 @@ protected String getAuthorizationBaseUrl() { public TokenExtractor getAccessTokenExtractor() { return OAuth2AccessTokenExtractor.instance(); } + + @Override + public BearerSignature getBearerSignature() { + return BearerSignatureURIQueryParameter.instance(); + } } diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/TheThingsNetworkV1StagingApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/TheThingsNetworkV1StagingApi.java new file mode 100644 index 000000000..844d75758 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/TheThingsNetworkV1StagingApi.java @@ -0,0 +1,27 @@ +package com.github.scribejava.apis; + +import com.github.scribejava.core.builder.api.DefaultApi20; + +public class TheThingsNetworkV1StagingApi extends DefaultApi20 { + + protected TheThingsNetworkV1StagingApi() { + } + + private static class InstanceHolder { + private static final TheThingsNetworkV1StagingApi INSTANCE = new TheThingsNetworkV1StagingApi(); + } + + public static TheThingsNetworkV1StagingApi instance() { + return InstanceHolder.INSTANCE; + } + + @Override + public String getAccessTokenEndpoint() { + return "https://account.thethingsnetwork.org/users/token"; + } + + @Override + protected String getAuthorizationBaseUrl() { + return "https://account.thethingsnetwork.org/users/authorize"; + } +} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/TheThingsNetworkV2PreviewApi.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/TheThingsNetworkV2PreviewApi.java new file mode 100644 index 000000000..1ac5437a0 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/TheThingsNetworkV2PreviewApi.java @@ -0,0 +1,27 @@ +package com.github.scribejava.apis; + +import com.github.scribejava.core.builder.api.DefaultApi20; + +public class TheThingsNetworkV2PreviewApi extends DefaultApi20 { + + protected TheThingsNetworkV2PreviewApi() { + } + + private static class InstanceHolder { + private static final TheThingsNetworkV2PreviewApi INSTANCE = new TheThingsNetworkV2PreviewApi(); + } + + public static TheThingsNetworkV2PreviewApi instance() { + return InstanceHolder.INSTANCE; + } + + @Override + public String getAccessTokenEndpoint() { + return "https://preview.account.thethingsnetwork.org/users/token"; + } + + @Override + protected String getAuthorizationBaseUrl() { + return "https://preview.account.thethingsnetwork.org/users/authorize"; + } +} 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 e3143b5b4..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 @@ -2,6 +2,8 @@ import com.github.scribejava.core.builder.api.DefaultApi20; 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 { @@ -30,4 +32,9 @@ public String getAccessTokenEndpoint() { protected String getAuthorizationBaseUrl() { return "https://secure.viadeo.com/oauth-provider/authorize2"; } + + @Override + 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 7d440d275..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,14 +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.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(); } @@ -28,6 +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 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 new file mode 100644 index 000000000..ea5053931 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/facebook/FacebookAccessTokenErrorResponse.java @@ -0,0 +1,99 @@ +package com.github.scribejava.apis.facebook; + +import com.github.scribejava.core.model.OAuthResponseException; +import com.github.scribejava.core.model.Response; +import java.io.IOException; +import java.util.Objects; + +/** + * non standard Facebook replace for {@link com.github.scribejava.core.model.OAuth2AccessTokenErrorResponse} + * + * examples:
+ * + * '{"error":{"message":"This authorization code has been + * used.","type":"OAuthException","code":100,"fbtrace_id":"DtxvtGRaxbB"}}'
+ * + * '{"error":{"message":"Error validating application. Invalid application + * ID.","type":"OAuthException","code":101,"fbtrace_id":"CvDR+X4WWIx"}}' + */ +public class FacebookAccessTokenErrorResponse extends OAuthResponseException { + + private static final long serialVersionUID = -1277129766099856895L; + + private final String errorMessage; + private final String type; + private final int codeInt; + private final String fbtraceId; + + public FacebookAccessTokenErrorResponse(String errorMessage, String type, int code, String fbtraceId, + Response response) + throws IOException { + super(response); + this.errorMessage = errorMessage; + this.type = type; + this.codeInt = code; + this.fbtraceId = fbtraceId; + } + + public String getErrorMessage() { + return errorMessage; + } + + public String getType() { + return type; + } + + public int getCodeInt() { + return codeInt; + } + + public String getFbtraceId() { + return fbtraceId; + } + + @Override + public int hashCode() { + int hash = super.hashCode(); + hash = 83 * hash + Objects.hashCode(errorMessage); + hash = 83 * hash + Objects.hashCode(type); + hash = 83 * hash + Objects.hashCode(codeInt); + hash = 83 * hash + Objects.hashCode(fbtraceId); + 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; + } + + final FacebookAccessTokenErrorResponse other = (FacebookAccessTokenErrorResponse) obj; + + if (!Objects.equals(errorMessage, other.getErrorMessage())) { + return false; + } + if (!Objects.equals(type, other.getType())) { + return false; + } + if (codeInt != other.getCodeInt()) { + return false; + } + return Objects.equals(fbtraceId, other.getFbtraceId()); + } + + @Override + public String toString() { + 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 new file mode 100644 index 000000000..f51935436 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/facebook/FacebookAccessTokenJsonExtractor.java @@ -0,0 +1,46 @@ +package com.github.scribejava.apis.facebook; + +import com.fasterxml.jackson.databind.JsonNode; +import com.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor; +import com.github.scribejava.core.model.Response; +import java.io.IOException; + +/** + * non standard Facebook Extractor + */ +public class FacebookAccessTokenJsonExtractor extends OAuth2AccessTokenJsonExtractor { + + protected FacebookAccessTokenJsonExtractor() { + } + + private static class InstanceHolder { + + private static final FacebookAccessTokenJsonExtractor INSTANCE = new FacebookAccessTokenJsonExtractor(); + } + + public static FacebookAccessTokenJsonExtractor instance() { + return InstanceHolder.INSTANCE; + } + + /** + * non standard. examples:
+ * + * '{"error":{"message":"This authorization code has been + * used.","type":"OAuthException","code":100,"fbtrace_id":"DtxvtGRaxbB"}}'
+ * + * '{"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()) + .get("error"); + + 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 8295ddc3f..000000000 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/google/GoogleJsonTokenExtractor.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.github.scribejava.apis.google; - -import com.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor; - -/** - * additionally parses OpenID id_token - */ -public class GoogleJsonTokenExtractor extends OAuth2AccessTokenJsonExtractor { - - private static final String ID_TOKEN_REGEX = "\"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, false), response); - } -} 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 new file mode 100644 index 000000000..1fc441a70 --- /dev/null +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/openid/OpenIdJsonTokenExtractor.java @@ -0,0 +1,30 @@ +package com.github.scribejava.apis.openid; + +import com.fasterxml.jackson.databind.JsonNode; +import com.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor; + +/** + * additionally parses OpenID id_token + */ +public class OpenIdJsonTokenExtractor extends OAuth2AccessTokenJsonExtractor { + + protected OpenIdJsonTokenExtractor() { + } + + private static class InstanceHolder { + + private static final OpenIdJsonTokenExtractor INSTANCE = new OpenIdJsonTokenExtractor(); + } + + public static OpenIdJsonTokenExtractor instance() { + return InstanceHolder.INSTANCE; + } + + @Override + protected OpenIdOAuth2AccessToken createToken(String accessToken, String tokenType, Integer expiresIn, + String refreshToken, String scope, JsonNode response, String rawResponse) { + final JsonNode idToken = response.get("id_token"); + return new OpenIdOAuth2AccessToken(accessToken, tokenType, expiresIn, refreshToken, scope, + idToken == null ? null : idToken.asText(), rawResponse); + } +} 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/openid/OpenIdOAuth2AccessToken.java similarity index 60% rename from scribejava-apis/src/main/java/com/github/scribejava/apis/google/GoogleToken.java rename to scribejava-apis/src/main/java/com/github/scribejava/apis/openid/OpenIdOAuth2AccessToken.java index 68ec31f82..7e6588444 100644 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/google/GoogleToken.java +++ b/scribejava-apis/src/main/java/com/github/scribejava/apis/openid/OpenIdOAuth2AccessToken.java @@ -1,11 +1,11 @@ -package com.github.scribejava.apis.google; +package com.github.scribejava.apis.openid; import com.github.scribejava.core.model.OAuth2AccessToken; import java.util.Objects; -public class GoogleToken extends OAuth2AccessToken { +public class OpenIdOAuth2AccessToken extends OAuth2AccessToken { - private static final long serialVersionUID = 7845679917727899612L; + private static final long serialVersionUID = -4534058186528117610L; /** * Id_token is part of OpenID Connect specification. It can hold user information that you can directly extract @@ -18,12 +18,12 @@ public class GoogleToken extends OAuth2AccessToken { */ private final String openIdToken; - public GoogleToken(String accessToken, String openIdToken, String rawResponse) { + public OpenIdOAuth2AccessToken(String accessToken, String openIdToken, String rawResponse) { this(accessToken, null, null, null, null, openIdToken, rawResponse); } - public GoogleToken(String accessToken, String tokenType, Integer expiresIn, String refreshToken, String scope, - String openIdToken, String rawResponse) { + public OpenIdOAuth2AccessToken(String accessToken, String tokenType, Integer expiresIn, String refreshToken, + String scope, String openIdToken, String rawResponse) { super(accessToken, tokenType, expiresIn, refreshToken, scope, rawResponse); this.openIdToken = openIdToken; } @@ -54,17 +54,6 @@ public boolean equals(Object obj) { return false; } - return Objects.equals(openIdToken, ((GoogleToken) obj).getOpenIdToken()); - } - - @Override - public String toString() { - return "GoogleToken{" - + "access_token=" + getAccessToken() - + ", token_type=" + getTokenType() - + ", expires_in=" + getExpiresIn() - + ", refresh_token=" + getRefreshToken() - + ", scope=" + getScope() - + ", open_id_token=" + openIdToken + '}'; + return Objects.equals(openIdToken, ((OpenIdOAuth2AccessToken) obj).getOpenIdToken()); } } 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 2f219163a..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,5 +1,6 @@ package com.github.scribejava.apis.salesforce; +import com.fasterxml.jackson.databind.JsonNode; import com.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor; /** @@ -8,8 +9,6 @@ */ public class SalesforceJsonTokenExtractor extends OAuth2AccessTokenJsonExtractor { - private static final String INSTANCE_URL_REGEX = "\"instance_url\"\\s*:\\s*\"(\\S*?)\""; - protected SalesforceJsonTokenExtractor() { } @@ -24,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, 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/DoktornaraboteOAuthServiceImpl.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/service/DoktornaraboteOAuthServiceImpl.java deleted file mode 100644 index 837449436..000000000 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/service/DoktornaraboteOAuthServiceImpl.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.github.scribejava.apis.service; - -import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.model.AbstractRequest; -import com.github.scribejava.core.model.OAuth2AccessToken; -import com.github.scribejava.core.model.OAuthConfig; -import com.github.scribejava.core.oauth.OAuth20Service; - -public class DoktornaraboteOAuthServiceImpl extends OAuth20Service { - - public DoktornaraboteOAuthServiceImpl(DefaultApi20 api, OAuthConfig config) { - super(api, config); - } - - @Override - public void signRequest(OAuth2AccessToken accessToken, AbstractRequest request) { - request.addHeader("Authorization", "Bearer " + accessToken.getAccessToken()); - } -} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/service/GeniusOAuthServiceImpl.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/service/GeniusOAuthServiceImpl.java deleted file mode 100644 index 4c2aa992c..000000000 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/service/GeniusOAuthServiceImpl.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.github.scribejava.apis.service; - -import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.model.AbstractRequest; -import com.github.scribejava.core.model.OAuth2AccessToken; -import com.github.scribejava.core.model.OAuthConfig; -import com.github.scribejava.core.oauth.OAuth20Service; - -public class GeniusOAuthServiceImpl extends OAuth20Service { - - public GeniusOAuthServiceImpl(DefaultApi20 api, OAuthConfig config) { - super(api, config); - } - - @Override - public void signRequest(OAuth2AccessToken accessToken, AbstractRequest request) { - request.addHeader("Authorization", "Bearer " + accessToken.getAccessToken()); - } -} diff --git a/scribejava-apis/src/main/java/com/github/scribejava/apis/service/HHOAuthServiceImpl.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/service/HHOAuthServiceImpl.java deleted file mode 100644 index 8d1799272..000000000 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/service/HHOAuthServiceImpl.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.github.scribejava.apis.service; - -import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.model.AbstractRequest; -import com.github.scribejava.core.model.OAuth2AccessToken; -import com.github.scribejava.core.model.OAuthConfig; -import com.github.scribejava.core.oauth.OAuth20Service; - -public class HHOAuthServiceImpl extends OAuth20Service { - - public HHOAuthServiceImpl(DefaultApi20 api, OAuthConfig config) { - super(api, config); - } - - @Override - public void signRequest(OAuth2AccessToken accessToken, AbstractRequest request) { - request.addHeader("Authorization", "Bearer " + accessToken.getAccessToken()); - } -} 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 ac80b1df6..000000000 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/service/ImgurOAuthServiceImpl.java +++ /dev/null @@ -1,39 +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.AbstractRequest; -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.oauth.OAuth20Service; - -public class ImgurOAuthServiceImpl extends OAuth20Service { - - public ImgurOAuthServiceImpl(DefaultApi20 api, OAuthConfig config) { - super(api, config); - } - - @Override - protected T createAccessTokenRequest(String oauthVerifier, T request) { - 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, AbstractRequest 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/LinkedIn20ServiceImpl.java b/scribejava-apis/src/main/java/com/github/scribejava/apis/service/LinkedIn20ServiceImpl.java deleted file mode 100644 index b0e6adf14..000000000 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/service/LinkedIn20ServiceImpl.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.github.scribejava.apis.service; - -import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.model.AbstractRequest; -import com.github.scribejava.core.model.OAuth2AccessToken; -import com.github.scribejava.core.model.OAuthConfig; -import com.github.scribejava.core.oauth.OAuth20Service; - -public class LinkedIn20ServiceImpl extends OAuth20Service { - - public LinkedIn20ServiceImpl(DefaultApi20 api, OAuthConfig config) { - super(api, config); - } - - @Override - public void signRequest(OAuth2AccessToken accessToken, AbstractRequest request) { - request.addQuerystringParameter("oauth2_access_token", 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 d436d8244..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.AbstractRequest; -import com.github.scribejava.core.model.OAuth2AccessToken; -import com.github.scribejava.core.model.OAuthConfig; -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, AbstractRequest 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 baca34604..000000000 --- a/scribejava-apis/src/main/java/com/github/scribejava/apis/service/OdnoklassnikiServiceImpl.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.github.scribejava.apis.service; - -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -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.AbstractRequest; -import com.github.scribejava.core.model.OAuth2AccessToken; -import com.github.scribejava.core.model.OAuthConfig; -import com.github.scribejava.core.oauth.OAuth20Service; - -public class OdnoklassnikiServiceImpl extends OAuth20Service { - - public OdnoklassnikiServiceImpl(DefaultApi20 api, OAuthConfig config) { - super(api, config); - } - - @Override - public void signRequest(OAuth2AccessToken accessToken, AbstractRequest request) { - // sig = md5( request_params_composed_string+ md5(access_token + application_secret_key) ) - try { - final String tokenDigest = md5Hex(accessToken.getAccessToken() + getConfig().getApiSecret()); - - final String completeUrl = request.getCompleteUrl(); - final int queryIndex = completeUrl.indexOf('?'); - if (queryIndex != -1) { - final String sigSource - = URLDecoder.decode(completeUrl.substring(queryIndex + 1).replace("&", ""), CharEncoding.UTF_8) - + tokenDigest; - request.addQuerystringParameter("sig", md5Hex(sigSource)); - } - - 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 21a61d13e..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.AbstractRequest; -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.oauth.OAuth20Service; - -public class TutByOAuthServiceImpl extends OAuth20Service { - - public TutByOAuthServiceImpl(DefaultApi20 api, OAuthConfig config) { - super(api, config); - } - - @Override - public void signRequest(OAuth2AccessToken accessToken, AbstractRequest 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 b17637c13..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 @@ -10,8 +10,9 @@ import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth10aService; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract 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/"; @@ -19,9 +20,12 @@ public abstract class AWeberExample { private static final String CONSUMER_KEY = ""; private static final String CONSUMER_SECRET = ""; - public static void main(String... args) throws IOException { - final OAuth10aService service = new ServiceBuilder() - .apiKey(CONSUMER_KEY) + 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) .build(AWeberApi.instance()); @@ -43,22 +47,22 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, ACCOUNT_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - 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 new file mode 100644 index 000000000..11673af20 --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/Box20Example.java @@ -0,0 +1,91 @@ +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.BoxApi20; +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 Box20Example { + + private static final String NETWORK_NAME = "Box"; + private static final String PROTECTED_RESOURCE_URL = "https://api.box.com/2.0/users/me"; + + 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"; + final String clientSecret = "your client secret"; + final String secretState = "security_token" + new Random().nextInt(999_999); + final OAuth20Service service = new ServiceBuilder(clientId) + .apiSecret(clientSecret) + .callback("https://example.com/callback") + .build(BoxApi20.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 + 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() + "')"); + + 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("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 f55c105a0..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 @@ -10,18 +10,22 @@ import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth10aService; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract 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"; - public static void main(String... args) throws IOException { + 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"; final String apiSecret = "mySecret"; - final OAuth10aService service = new ServiceBuilder() - .apiKey(apiKey) + final OAuth10aService service = new ServiceBuilder(apiKey) .apiSecret(apiSecret) .build(DiggApi.instance()); final Scanner in = new Scanner(System.in); @@ -46,25 +50,24 @@ public static void main(String... args) throws IOException { 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.POST, PROTECTED_RESOURCE_URL, service); + final OAuthRequest request = new OAuthRequest(Verb.POST, PROTECTED_RESOURCE_URL); request.addBodyParameter("comment_id", "20100729223726:4fef610331ee46a3b5cbd740bf71313e"); service.signRequest(accessToken, request); - final Response response = request.send(); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); - + 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 7a1772aec..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 @@ -10,21 +10,24 @@ import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth20Service; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract class FacebookExample { +public class FacebookExample { private static final String NETWORK_NAME = "Facebook"; - private static final String PROTECTED_RESOURCE_URL = "https://graph.facebook.com/v2.6/me"; + private static final String PROTECTED_RESOURCE_URL = "https://graph.facebook.com/v3.2/me"; - public static void main(String... args) throws IOException { + 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"; final String clientSecret = "your client secret"; final String secretState = "secret" + new Random().nextInt(999_999); - final OAuth20Service service = new ServiceBuilder() - .apiKey(clientId) + final OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) - .state(secretState) .callback("http://www.example.com/oauth_callback/") .build(FacebookApi.instance()); @@ -35,7 +38,7 @@ public static void main(String... args) throws IOException { // 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); @@ -56,24 +59,22 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); - + 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 ed03b906d..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 @@ -1,28 +1,34 @@ package com.github.scribejava.apis.examples; -import java.util.Scanner; -import com.github.scribejava.core.builder.ServiceBuilder; import com.github.scribejava.apis.FlickrApi; +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 abstract class FlickrExample { +public class FlickrExample { private static final String PROTECTED_RESOURCE_URL = "http://api.flickr.com/services/rest/"; - public static void main(String... args) throws IOException { + 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"; final String apiSecret = "your_api_secret"; - final OAuth10aService service = new ServiceBuilder() - .apiKey(apiKey) + + final OAuth10aService service = new ServiceBuilder(apiKey) .apiSecret(apiSecret) - .build(FlickrApi.instance()); + .build(FlickrApi.instance(FlickrApi.FlickrPerm.DELETE)); final Scanner in = new Scanner(System.in); System.out.println("=== Flickr's OAuth Workflow ==="); @@ -36,30 +42,29 @@ public static void main(String... args) throws IOException { System.out.println("Now go and authorize ScribeJava here:"); final String authorizationUrl = service.getAuthorizationUrl(requestToken); - System.out.println(authorizationUrl + "&perms=read"); + System.out.println(authorizationUrl); 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); request.addQuerystringParameter("method", "flickr.test.login"); service.signRequest(accessToken, request); - final Response response = request.send(); - 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 c9b317911..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 @@ -9,18 +9,22 @@ import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth20Service; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract class Foursquare2Example { +public class Foursquare2Example { private static final String PROTECTED_RESOURCE_URL = "https://api.foursquare.com/v2/users/self/friends?oauth_token="; - public static void main(String... args) throws IOException { + 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"; final String apiSecret = "your client secret"; - final OAuth20Service service = new ServiceBuilder() - .apiKey(apiKey) + final OAuth20Service service = new ServiceBuilder(apiKey) .apiSecret(apiSecret) .callback("http://localhost:9000/") .build(Foursquare2Api.instance()); @@ -40,25 +44,22 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL + accessToken.getAccessToken()); service.signRequest(accessToken, request); - final Response response = request.send(); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); - + 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 639aaec0a..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 @@ -10,14 +10,18 @@ import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth10aService; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract class FoursquareExample { +public class FoursquareExample { private static final String PROTECTED_RESOURCE_URL = "http://api.foursquare.com/v1/user"; - public static void main(String... args) throws IOException { - final OAuth10aService service = new ServiceBuilder() - .apiKey("your client id") + 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") .build(FoursquareApi.instance()); final Scanner in = new Scanner(System.in); @@ -38,23 +42,22 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - 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 new file mode 100644 index 000000000..827f8aacd --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/FrappeExample.java @@ -0,0 +1,68 @@ +package com.github.scribejava.apis.examples; + +import java.util.Scanner; +import com.github.scribejava.core.builder.ServiceBuilder; +import com.github.scribejava.apis.FrappeApi; +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 FrappeExample { + + private static final String NETWORK_NAME = "Frappe"; + private static final String PROTECTED_RESOURCE_PATH = "/api/method/frappe.integrations.oauth2.openid_profile"; + + 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"; + final String clientSecret = "clientSecret"; + final String clientDomain = "https://example.com"; + final OAuth20Service service = new ServiceBuilder(clientId) + .apiSecret(clientSecret) + .defaultScope("openid all") + .callback("https://example.com/callback") + .build(FrappeApi.instance(clientDomain)); + final Scanner in = new Scanner(System.in, "UTF-8"); + + System.out.println("=== " + NETWORK_NAME + "'s OAuth 2.0 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(); + + // Trade the Authorization Code 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, clientDomain + PROTECTED_RESOURCE_PATH); + 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("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 54e0a7d8d..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 @@ -7,12 +7,12 @@ 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.SignatureType; import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth10aService; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract class FreelancerExample { +public class FreelancerExample { private static final String NETWORK_NAME = "Freelancer"; private static final String AUTHORIZE_URL @@ -20,12 +20,14 @@ public abstract class FreelancerExample { private static final String PROTECTED_RESOURCE_URL = "http://api.sandbox.freelancer.com/Job/getJobList.json"; private static final String SCOPE = "http://api.sandbox.freelancer.com"; - public static void main(String... args) throws IOException { - final OAuth10aService service = new ServiceBuilder() - .signatureType(SignatureType.QueryString) - .apiKey("your client id") + 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); @@ -36,7 +38,7 @@ public static void main(String... args) throws IOException { 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:"); @@ -46,25 +48,24 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); request.addHeader("GData-Version", "3.0"); - final Response response = request.send(); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); - + 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 0db6ddc05..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 @@ -10,22 +10,25 @@ import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth20Service; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract 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"; - public static void main(String... args) throws IOException { + 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"; final String clientSecret = "your client secret"; final String secretState = "100"; - final OAuth20Service service = new ServiceBuilder() - .apiKey(clientId) + final OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) - .scope("me") - .state(secretState) + .defaultScope("me") .callback("com.scribejavatest://callback") .userAgent("ScribeJava") .build(GeniusApi.instance()); @@ -35,7 +38,7 @@ public static void main(String... args) throws IOException { // 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); @@ -72,14 +75,14 @@ public static void main(String... args) throws IOException { // Now let's go and ask for a protected resource! System.out.println("Accessing a protected resource..."); - final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL, service); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - 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 new file mode 100644 index 000000000..a0c94792c --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/GitHubAsyncOkHttpExample.java @@ -0,0 +1,86 @@ +package com.github.scribejava.apis.examples; + +import com.github.scribejava.apis.GitHubApi; +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.okhttp.OkHttpHttpClientConfig; + +import java.io.IOException; +import java.util.Random; +import java.util.Scanner; +import java.util.concurrent.ExecutionException; + +public class GitHubAsyncOkHttpExample { + + private static final String NETWORK_NAME = "GitHub"; + private static final String PROTECTED_RESOURCE_URL = "https://api.github.com/user"; + + 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"; + 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(OkHttpHttpClientConfig.defaultConfig()) + .build(GitHubApi.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/GitHubExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/GitHubExample.java index 0a07c9709..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 @@ -10,21 +10,24 @@ import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth20Service; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract class GitHubExample { +public class GitHubExample { private static final String NETWORK_NAME = "GitHub"; private static final String PROTECTED_RESOURCE_URL = "https://api.github.com/user"; - public static void main(String... args) throws IOException { + 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"; final String clientSecret = "your client secret"; final String secretState = "secret" + new Random().nextInt(999_999); - final OAuth20Service service = new ServiceBuilder() - .apiKey(clientId) + 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"); @@ -34,7 +37,7 @@ public static void main(String... args) throws IOException { // 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); @@ -55,24 +58,22 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); - + 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 6ad067acf..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 @@ -3,121 +3,124 @@ import java.util.Random; import java.util.Scanner; import com.github.scribejava.apis.GoogleApi20; +import com.github.scribejava.httpclient.ahc.AhcHttpClientConfig; import com.github.scribejava.core.builder.ServiceBuilder; -import com.github.scribejava.core.model.ForceTypeOfHttpRequest; +import com.github.scribejava.core.httpclient.HttpClientConfig; import com.github.scribejava.core.model.OAuth2AccessToken; -import com.github.scribejava.core.model.OAuthRequestAsync; +import com.github.scribejava.core.model.OAuthRequest; import com.github.scribejava.core.model.Response; -import com.github.scribejava.core.model.ScribeJavaConfig; 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; -import org.asynchttpclient.AsyncHttpClientConfig; import org.asynchttpclient.DefaultAsyncHttpClientConfig; -public abstract 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"; final String clientSecret = "your client secret"; final String secretState = "secret" + new Random().nextInt(999_999); - ScribeJavaConfig.setForceTypeOfHttpRequests(ForceTypeOfHttpRequest.FORCE_ASYNC_ONLY_HTTP_REQUESTS); - final AsyncHttpClientConfig clientConfig = new DefaultAsyncHttpClientConfig.Builder() + final HttpClientConfig clientConfig = new AhcHttpClientConfig(new DefaultAsyncHttpClientConfig.Builder() .setMaxConnections(5) .setRequestTimeout(10_000) .setPooledConnectionIdleTimeout(1_000) .setReadTimeout(1_000) - .build(); + .build()); - final OAuth20Service service = new ServiceBuilder() - .apiKey(clientId) + 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") - .asyncAHCHttpClientConfig(clientConfig) - .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 usera are asked not the first time) - additionalParams.put("prompt", "consent"); - final String authorizationUrl = service.getAuthorizationUrl(additionalParams); - 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(); + .httpClientConfig(clientConfig) + .build(GoogleApi20.instance())) { + final Scanner in = new Scanner(System.in, "UTF-8"); - 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("=== " + NETWORK_NAME + "'s OAuth Workflow ==="); System.out.println(); - } - - // Trade the Request Token and Verfier for the Access Token - System.out.println("Trading the Request Token for an Access Token..."); - OAuth2AccessToken accessToken = service.getAccessTokenAsync(code, null).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("Refreshing the Access Token..."); - accessToken = service.refreshAccessTokenAsync(accessToken.getRefreshToken(), null).get(); - 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..."); - while (true) { - System.out.println("Paste fieldnames to fetch (leave empty to get profile, 'exit' to stop example)"); + // 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 query = in.nextLine(); + final String code = in.nextLine(); System.out.println(); - final String requestUrl; - if ("exit".equals(query)) { - break; - } else if (query == null || query.isEmpty()) { - requestUrl = PROTECTED_RESOURCE_URL; + 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 { - requestUrl = PROTECTED_RESOURCE_URL + "?fields=" + query; + System.out.println("Ooops, state value does not match!"); + System.out.println("Expected = " + secretState); + System.out.println("Got = " + value); + System.out.println(); } - final OAuthRequestAsync request = new OAuthRequestAsync(Verb.GET, requestUrl, service); - service.signRequest(accessToken, request); - final Response response = request.sendAsync(null).get(); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); + 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(); + } } - service.closeAsyncClient(); } } 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 2e6800c19..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 @@ -12,22 +12,25 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ExecutionException; -public abstract 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"; - public static void main(String... args) throws IOException { + 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() - .apiKey(clientId) + 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"); @@ -41,9 +44,12 @@ public static void main(String... args) throws IOException { //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); @@ -64,18 +70,15 @@ public static void main(String... args) throws IOException { 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! @@ -95,13 +98,13 @@ public static void main(String... args) throws IOException { requestUrl = PROTECTED_RESOURCE_URL + "?fields=" + query; } - final OAuthRequest request = new OAuthRequest(Verb.GET, requestUrl, service); + final OAuthRequest request = new OAuthRequest(Verb.GET, requestUrl); service.signRequest(accessToken, request); - final Response response = request.send(); 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 add150d1f..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 @@ -11,18 +11,22 @@ import com.github.scribejava.core.model.OAuth2AccessToken; import com.github.scribejava.core.oauth.OAuth20Service; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract 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"; - public static void main(String... args) throws IOException { + 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"; final String clientSecret = "your client secret"; - final OAuth20Service service = new ServiceBuilder() - .apiKey(clientId) + final OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) .callback("http://your.site.com/callback") .build(HHApi.instance()); @@ -42,23 +46,21 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); - + 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 1d3f24a44..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 @@ -10,18 +10,22 @@ import java.io.IOException; import java.util.Scanner; +import java.util.concurrent.ExecutionException; -public abstract 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"; - public static void main(String... args) throws IOException { + 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"; final String apiSecret = "your client secret"; - final OAuth20Service service = new ServiceBuilder() - .apiKey(apiKey) + final OAuth20Service service = new ServiceBuilder(apiKey) .apiSecret(apiSecret) .build(ImgurApi.instance()); final Scanner in = new Scanner(System.in); @@ -44,20 +48,19 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); - + 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 4551370d6..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 @@ -9,18 +9,22 @@ import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth20Service; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract 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"; - public static void main(String... args) throws IOException { + 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"; final String apiSecret = "your api secret"; - final OAuth20Service service = new ServiceBuilder() - .apiKey(apiKey) + final OAuth20Service service = new ServiceBuilder(apiKey) .apiSecret(apiSecret) .callback("http://your.domain.com/handle") .build(KaixinApi20.instance()); @@ -44,20 +48,19 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); - + 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 f29d1907b..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,27 +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 abstract 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~))"; - public static void main(String... args) throws IOException { + 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() - .apiKey(clientId).apiSecret(clientSecret) - .scope("r_basicprofile r_emailaddress") // replace with desired scope + final OAuth20Service service = new ServiceBuilder(clientId) + .apiSecret(clientSecret) + .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); @@ -32,7 +40,8 @@ public static void main(String... args) throws IOException { // 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); @@ -41,37 +50,36 @@ public static void main(String... args) throws IOException { 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), - service); - request.addHeader("x-li-format", "json"); - request.addHeader("Accept-Language", "ru-RU"); - service.signRequest(accessToken, request); - final Response response = request.send(); - 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 81324377b..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 @@ -10,15 +10,19 @@ import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth10aService; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract class LinkedInExample { +public class LinkedInExample { private static final String PROTECTED_RESOURCE_URL = "http://api.linkedin.com/v1/people/~/connections:(id,last-name)"; - public static void main(String... args) throws IOException { - final OAuth10aService service = new ServiceBuilder() - .apiKey("your client id") + 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") .build(LinkedInApi.instance()); final Scanner in = new Scanner(System.in); @@ -39,22 +43,22 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - 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 2e951fa5f..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 @@ -10,21 +10,25 @@ import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth10aService; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract class LinkedInExampleWithScopes { +public class LinkedInExampleWithScopes { private static final String PROTECTED_RESOURCE_URL = "http://api.linkedin.com/v1/people/~/connections:(id,last-name)"; - public static void main(String... args) throws IOException { + 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"; final String clientSecret = "your client id"; - final OAuth10aService service = new ServiceBuilder() - .apiKey(clientId) + final OAuth10aService service = new ServiceBuilder(clientId) .apiSecret(clientSecret) - .build(new LinkedInApi("foo", "bar", "baz")); + .build(LinkedInApi.instance("foo", "bar", "baz")); final Scanner in = new Scanner(System.in); System.out.println("=== LinkedIn's OAuth Workflow ==="); @@ -43,22 +47,22 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - 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 0540a26db..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 @@ -9,20 +9,23 @@ import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth20Service; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract class LiveExample { +public class LiveExample { - private static final String PROTECTED_RESOURCE_URL - = "https://api.foursquare.com/v2/users/self/friends?oauth_token="; + private static final String PROTECTED_RESOURCE_URL = "https://apis.live.net/v5.0/me"; - public static void main(String... args) throws IOException { + 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(apiKey) + 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); @@ -41,24 +44,22 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); + 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 6ca98d697..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 @@ -1,79 +1,82 @@ package com.github.scribejava.apis.examples; +import com.github.scribejava.httpclient.ning.NingHttpClientConfig; import com.ning.http.client.AsyncHttpClientConfig; import java.util.Scanner; import java.util.concurrent.ExecutionException; import com.github.scribejava.apis.MailruApi; import com.github.scribejava.core.builder.ServiceBuilder; import com.github.scribejava.core.model.OAuth2AccessToken; -import com.github.scribejava.core.model.OAuthRequestAsync; +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; -public abstract class MailruAsyncExample { +public class MailruAsyncExample { private static final String NETWORK_NAME = "Mail.ru"; private static final String PROTECTED_RESOURCE_URL = "http://www.appsmail.ru/platform/api?method=users.getInfo&secure=1"; + 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"; final String clientSecret = "your client secret"; - final AsyncHttpClientConfig clientConfig = new AsyncHttpClientConfig.Builder() + final NingHttpClientConfig clientConfig = new NingHttpClientConfig(new AsyncHttpClientConfig.Builder() .setMaxConnections(5) .setRequestTimeout(10_000) .setAllowPoolingConnections(false) .setPooledConnectionIdleTimeout(1_000) .setReadTimeout(10_000) - .build(); + .build()); - final OAuth20Service service = new ServiceBuilder() - .apiKey(clientId) + try (OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) .callback("http://www.example.com/oauth_callback/") - .asyncNingHttpClientConfig(clientConfig) - .build(MailruApi.instance()); - - final Scanner in = new Scanner(System.in, "UTF-8"); + .httpClientConfig(clientConfig) + .build(MailruApi.instance())) { + final Scanner in = new Scanner(System.in, "UTF-8"); - System.out.println("=== " + NETWORK_NAME + "'s Async OAuth Workflow ==="); - System.out.println(); + 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(); - 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(); + // 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(); - // 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.getAccessTokenAsync(code, null).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(); + 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 OAuthRequestAsync request = new OAuthRequestAsync(Verb.GET, PROTECTED_RESOURCE_URL, service); - service.signRequest(accessToken, request); - final Response response = request.sendAsync(null).get(); + 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); - 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! :)"); - service.closeAsyncClient(); + 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 ae4ad4856..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 @@ -9,19 +9,23 @@ import com.github.scribejava.core.model.OAuth2AccessToken; import com.github.scribejava.core.oauth.OAuth20Service; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract class MailruExample { +public class MailruExample { private static final String NETWORK_NAME = "Mail.ru"; private static final String PROTECTED_RESOURCE_URL = "http://www.appsmail.ru/platform/api?method=users.getInfo&secure=1"; - public static void main(String... args) throws IOException { + 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"; final String clientSecret = "your client secret"; - final OAuth20Service service = new ServiceBuilder() - .apiKey(clientId) + final OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) .callback("http://www.example.com/oauth_callback/") .build(MailruApi.instance()); @@ -42,22 +46,21 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); + 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 64180b980..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 @@ -10,14 +10,18 @@ import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth10aService; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract class MeetupExample { +public class MeetupExample { private static final String PROTECTED_RESOURCE_URL = "http://api.meetup.com/2/member/self"; - public static void main(String... args) throws IOException { - final OAuth10aService service = new ServiceBuilder() - .apiKey("your client id") + 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") .build(MeetupApi.instance()); final Scanner in = new Scanner(System.in); @@ -38,22 +42,22 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - 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 66e5901c9..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 @@ -10,22 +10,26 @@ import java.io.IOException; import java.util.Scanner; +import java.util.concurrent.ExecutionException; -public abstract class MisfitExample { +public class MisfitExample { private static final String NETWORK_NAME = "Misfit"; private static final String PROTECTED_RESOURCE_URL = "https://api.misfitwearables.com/move/resource/v1/user/me/profile"; - public static void main(String... args) throws IOException { + 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"; final String apiSecret = "your client secret"; - final OAuth20Service service = new ServiceBuilder() - .apiKey(apiKey) + 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); @@ -47,19 +51,19 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); + 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/NaverExample.java similarity index 55% 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/NaverExample.java index 373759c35..7918ebd02 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/NaverExample.java @@ -1,55 +1,45 @@ package com.github.scribejava.apis.examples; -import com.ning.http.client.AsyncHttpClientConfig; -import java.util.Random; -import java.util.Scanner; -import java.util.concurrent.ExecutionException; -import com.github.scribejava.apis.FacebookApi; +import com.github.scribejava.apis.NaverApi; import com.github.scribejava.core.builder.ServiceBuilder; -import com.github.scribejava.core.model.ForceTypeOfHttpRequest; import com.github.scribejava.core.model.OAuth2AccessToken; -import com.github.scribejava.core.model.OAuthRequestAsync; +import com.github.scribejava.core.model.OAuthRequest; import com.github.scribejava.core.model.Response; -import com.github.scribejava.core.model.ScribeJavaConfig; 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 abstract class FacebookAsyncNingExample { +public class NaverExample { - private static final String NETWORK_NAME = "Facebook"; - private static final String PROTECTED_RESOURCE_URL = "https://graph.facebook.com/v2.6/me"; + private static final String NETWORK_NAME = "Naver"; + private static final String PROTECTED_RESOURCE_URL = "https://openapi.naver.com/v1/nid/me"; - public static void main(String... args) throws InterruptedException, ExecutionException, IOException { + 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"; final String clientSecret = "your client secret"; final String secretState = "secret" + new Random().nextInt(999_999); - ScribeJavaConfig.setForceTypeOfHttpRequests(ForceTypeOfHttpRequest.FORCE_ASYNC_ONLY_HTTP_REQUESTS); - final AsyncHttpClientConfig clientConfig = new AsyncHttpClientConfig.Builder() - .setMaxConnections(5) - .setRequestTimeout(10_000) - .setAllowPoolingConnections(false) - .setPooledConnectionIdleTimeout(1_000) - .setReadTimeout(1_000) - .build(); - - final OAuth20Service service = new ServiceBuilder() - .apiKey(clientId) + final OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) - .state(secretState) .callback("http://www.example.com/oauth_callback/") - .asyncNingHttpClientConfig(clientConfig) - .build(FacebookApi.instance()); + .build(NaverApi.instance()); final Scanner in = new Scanner(System.in, "UTF-8"); - System.out.println("=== " + NETWORK_NAME + "'s Async OAuth Workflow ==="); + 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(); + 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,26 +60,25 @@ 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..."); - final OAuth2AccessToken accessToken = service.getAccessTokenAsync(code, null).get(); + 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 OAuthRequestAsync request = new OAuthRequestAsync(Verb.GET, PROTECTED_RESOURCE_URL, service); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.sendAsync(null).get(); - 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! :)"); - service.closeAsyncClient(); + } } 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 08f782b13..000000000 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/NeteaseWeiboExample.java +++ /dev/null @@ -1,70 +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; - -public abstract 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"; - - public static void main(String... args) throws IOException { - // 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(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); - service.signRequest(accessToken, request); - final Response response = request.send(); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); - - System.out.println(); - System.out.println("Thats it man! Go and build something awesome with 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 9996484a4..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 @@ -9,21 +9,25 @@ import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth20Service; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract class OdnoklassnikiExample { +public class OdnoklassnikiExample { private static final String NETWORK_NAME = "Odnoklassniki.ru"; private static final String PROTECTED_RESOURCE_URL = "https://api.ok.ru/api/users/getCurrentUser?application_key=%1$s&format=JSON"; - public static void main(String... args) throws IOException { + 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"; - final OAuth20Service service = new ServiceBuilder() - .apiKey(clientId) + final OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(secretKey) .callback("http://your.site.com/callback") .build(OdnoklassnikiApi.instance()); @@ -44,32 +48,28 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, String.format(PROTECTED_RESOURCE_URL, publicKey)); service.signRequest(accessToken, request); - final Response response = request.send(); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); + 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 ca0359611..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 @@ -10,20 +10,24 @@ import java.io.IOException; import java.util.Scanner; +import java.util.concurrent.ExecutionException; -public abstract 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/"; - public static void main(String... args) throws IOException { + 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(apiKey) + 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); @@ -41,24 +45,22 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); + 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 4fbda56ff..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 @@ -10,14 +10,18 @@ import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth10aService; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract class Px500Example { +public class Px500Example { private static final String PROTECTED_RESOURCE_URL = "https://api.500px.com/v1/"; - public static void main(String... args) throws IOException { - final OAuth10aService service = new ServiceBuilder() - .apiKey("your-api-key") + 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") .build(Px500Api.instance()); final Scanner in = new Scanner(System.in); @@ -38,22 +42,22 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - 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 42f99eb6d..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,20 +15,28 @@ 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 abstract 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"; - public static void main(String... args) throws IOException { + 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(apiKey) + 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); @@ -50,42 +55,43 @@ public static void main(String... args) throws IOException { 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.POST, PROTECTED_RESOURCE_URL, service); + final OAuthRequest request = new OAuthRequest(Verb.POST, PROTECTED_RESOURCE_URL); final Map parameters = new HashMap<>(); parameters.put("method", "users.getInfo"); parameters.put("format", "json"); 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 = request.send(); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); + 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! :)"); @@ -96,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 1189fa30c..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 @@ -8,22 +8,29 @@ import com.github.scribejava.apis.SalesforceApi; import com.github.scribejava.apis.salesforce.SalesforceToken; 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.security.KeyManagementException; import java.security.NoSuchAlgorithmException; +import java.util.concurrent.ExecutionException; -public abstract class SalesforceExample { +public class SalesforceExample { private static final String NETWORK_NAME = "Salesforce"; - public static void main(String... args) throws IOException, NoSuchAlgorithmException, KeyManagementException { + 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(); @@ -32,9 +39,7 @@ 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() - .apiKey(clientId) + final OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) .callback("https://www.example.com/callback") .build(SalesforceApi.instance()); @@ -61,13 +66,20 @@ public static void main(String... args) throws IOException, NoSuchAlgorithmExcep // Trade the Request Token and Verifier for the Access Token System.out.println("Trading the Request Token for an Access Token..."); - final SalesforceToken accessToken = (SalesforceToken) service.getAccessToken(codeEncoded); + + final OAuth2AccessToken accessToken = service.getAccessToken(codeEncoded); + final SalesforceToken salesforceAccessToken; + if (accessToken instanceof SalesforceToken) { + salesforceAccessToken = (SalesforceToken) accessToken; + } else { + throw new IllegalStateException("Salesforce API didn't return SalesforceToken."); + } 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("instance_url is: " + accessToken.getInstanceUrl()); + System.out.println("instance_url is: " + salesforceAccessToken.getInstanceUrl()); // Now let's go and ask for a protected resource! System.out.println("Now we're reading accounts from the Salesforce org (maxing them to 10)."); @@ -76,16 +88,17 @@ public static void main(String... args) throws IOException, NoSuchAlgorithmExcep final String queryEncoded = URLEncoder.encode("Select Id, Name from Account LIMIT 10", "UTF-8"); // Building the query URI. We've parsed the instance URL from the accessToken request. - final String url = accessToken.getInstanceUrl() + "/services/data/v36.0/query?q=" + queryEncoded; + final String url = salesforceAccessToken.getInstanceUrl() + "/services/data/v36.0/query?q=" + queryEncoded; System.out.println(); System.out.println("Full URL: " + url); - final OAuthRequest request = new OAuthRequest(Verb.GET, url, service); - request.addHeader("Authorization", "Bearer " + accessToken.getAccessToken()); - final Response response = request.send(); + final OAuthRequest request = new OAuthRequest(Verb.GET, url); + 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 40d72f9da..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 @@ -9,95 +9,93 @@ import com.github.scribejava.apis.SalesforceApi; import com.github.scribejava.apis.salesforce.SalesforceToken; +import com.github.scribejava.httpclient.ning.NingHttpClientConfig; import com.github.scribejava.core.builder.ServiceBuilder; -import com.github.scribejava.core.model.ForceTypeOfHttpRequest; -import com.github.scribejava.core.model.OAuthRequestAsync; +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.ScribeJavaConfig; import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth20Service; import com.ning.http.client.AsyncHttpClientConfig; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; -public abstract class SalesforceNingAsyncExample { +public class SalesforceNingAsyncExample { private static final String NETWORK_NAME = "Salesforce"; - @SuppressWarnings({"unchecked", "rawtypes"}) + private SalesforceNingAsyncExample() { + } + + @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 final String clientId = "your client id"; final String clientSecret = "your client secret"; - ScribeJavaConfig.setForceTypeOfHttpRequests(ForceTypeOfHttpRequest.FORCE_ASYNC_ONLY_HTTP_REQUESTS); - final AsyncHttpClientConfig clientConfig = new AsyncHttpClientConfig.Builder() + final NingHttpClientConfig clientConfig = new NingHttpClientConfig(new AsyncHttpClientConfig.Builder() .setMaxConnections(5) .setRequestTimeout(10_000) .setAllowPoolingConnections(false) .setPooledConnectionIdleTimeout(1_000) .setReadTimeout(10_000) - .build(); + .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(); - final OAuth20Service service = new ServiceBuilder() - .apiKey(clientId) + try (OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) - .asyncNingHttpClientConfig(clientConfig) + .httpClientConfig(clientConfig) .callback("https://www.example.com/callback") - .build(SalesforceApi.instance()); - - System.out.println("=== " + NETWORK_NAME + "'s OAuth20 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; - try (Scanner in = new Scanner(System.in)) { - code = in.nextLine(); + .build(SalesforceApi.instance())) { + System.out.println("=== " + NETWORK_NAME + "'s OAuth20 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; + try (Scanner in = new Scanner(System.in)) { + code = in.nextLine(); + } + System.out.println(); + final String codeEncoded = URLDecoder.decode(code, "UTF-8"); + // 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(codeEncoded); + final SalesforceToken salesforceAccessToken; + if (accessToken instanceof SalesforceToken) { + salesforceAccessToken = (SalesforceToken) accessToken; + } else { + throw new IllegalStateException("Salesforce API didn't return SalesforceToken."); + } + System.out.println("Got the Access Token!"); + + 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! + System.out.println("Now we're reading accounts from the Salesforce org (maxing them to 10)."); + // Sample SOQL statement + final String queryEncoded = URLEncoder.encode("Select Id, Name from Account LIMIT 10", "UTF-8"); + // Building the query URI. We've parsed the instance URL from the + // accessToken request. + final String url = salesforceAccessToken.getInstanceUrl() + "/services/data/v36.0/query?q=" + queryEncoded; + System.out.println(); + System.out.println("Full URL: " + url); + final OAuthRequest request = new OAuthRequest(Verb.GET, url); + System.out.println(); + try (Response response = service.execute(request)) { + System.out.println(response.getCode()); + System.out.println(response.getBody()); + } } - System.out.println(); - - final String codeEncoded = URLDecoder.decode(code, "UTF-8"); - - // Trade the Request Token and Verifier for the Access Token - System.out.println("Trading the Request Token for an Access Token..."); - final SalesforceToken accessToken = (SalesforceToken) service.getAccessTokenAsync(codeEncoded, null).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(); - - System.out.println("Instance is: " + accessToken.getInstanceUrl()); - - // Now let's go and ask for a protected resource! - System.out.println("Now we're reading accounts from the Salesforce org (maxing them to 10)."); - - // Sample SOQL statement - final String queryEncoded = URLEncoder.encode("Select Id, Name from Account LIMIT 10", "UTF-8"); - - // Building the query URI. We've parsed the instance URL from the - // accessToken request. - final String url = accessToken.getInstanceUrl() + "/services/data/v36.0/query?q=" + queryEncoded; - - System.out.println(); - System.out.println("Full URL: " + url); - - final OAuthRequestAsync request = new OAuthRequestAsync(Verb.GET, url, service); - request.addHeader("Authorization", "Bearer " + accessToken.getAccessToken()); - final Response response = request.sendAsync(null).get(); - - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); - service.closeAsyncClient(); } } 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 815f19ba2..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 @@ -9,18 +9,22 @@ import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth20Service; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract 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"; - public static void main(String... args) throws IOException { + 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"; final String apiSecret = "your_api_secret"; - final OAuth20Service service = new ServiceBuilder() - .apiKey(apiKey) + final OAuth20Service service = new ServiceBuilder(apiKey) .apiSecret(apiSecret) .callback("http://www.dajie.com/oauth/sina") .build(SinaWeiboApi20.instance()); @@ -44,19 +48,19 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); + 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 8c3fccbd7..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 @@ -10,18 +10,22 @@ import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth10aService; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract 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"; - public static void main(String... args) throws IOException { + 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"; final String apiSecret = "your secret"; - final OAuth10aService service = new ServiceBuilder() - .apiKey(apiKey) + final OAuth10aService service = new ServiceBuilder(apiKey) .apiSecret(apiSecret) .build(SinaWeiboApi.instance()); final Scanner in = new Scanner(System.in); @@ -46,23 +50,23 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); + 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 440058d57..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 @@ -10,14 +10,18 @@ import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth10aService; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract class SkyrockExample { +public class SkyrockExample { private static final String PROTECTED_RESOURCE_URL = "https://api.skyrock.com/v2/user/get.json"; - public static void main(String... args) throws IOException { - final OAuth10aService service = new ServiceBuilder() - .apiKey("your-api-key") + 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") .build(SkyrockApi.instance()); final Scanner in = new Scanner(System.in); @@ -38,23 +42,23 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); + 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/SohuWeiboExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SohuWeiboExample.java deleted file mode 100644 index d91829387..000000000 --- a/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/SohuWeiboExample.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.github.scribejava.apis.examples; - -import java.util.Scanner; -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; - -public abstract class SohuWeiboExample { - - private static final String NETWORK_NAME = "SohuWeibo"; - private static final String PROTECTED_RESOURCE_URL = "http://api.t.sohu.com/account/verify_credentials.json"; - - public static void main(String... args) throws IOException { - // 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(apiKey) - .apiSecret(apiSecret) - .build(SohuWeiboApi.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); - service.signRequest(accessToken, request); - final Response response = request.send(); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); - - System.out.println(); - System.out.println("Thats it man! Go and build something awesome with 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 d7d2341a2..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 @@ -11,13 +11,18 @@ import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth20Service; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract 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"; - public static void main(String... args) throws IOException { + 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 final String clientId = "your client id"; @@ -26,10 +31,8 @@ public static void main(String... args) throws IOException { // Enter one of Stack Exchange site names the user has account with. final String site = "stackoverflow"; final String secretState = "secret" + new Random().nextInt(999_999); - final OAuth20Service service = new ServiceBuilder() - .apiKey(clientId) + 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"); @@ -39,7 +42,7 @@ public static void main(String... args) throws IOException { // 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,24 +63,23 @@ public static void main(String... args) throws IOException { 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 + "?site=" + site + "&key=" + key, service); + PROTECTED_RESOURCE_URL + "?site=" + site + "&key=" + key); service.signRequest(accessToken, request); - final Response response = request.send(); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); + 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 new file mode 100644 index 000000000..63139e589 --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TheThingsNetworkV1StagingExample.java @@ -0,0 +1,97 @@ +package com.github.scribejava.apis.examples; + +import com.github.scribejava.apis.TheThingsNetworkV1StagingApi; +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.net.URLDecoder; +import java.util.Random; +import java.util.Scanner; +import java.util.concurrent.ExecutionException; + +public class TheThingsNetworkV1StagingExample { + + private static final String NETWORK_NAME = "TTNv1staging"; + private static final String PROTECTED_RESOURCE_URL = "https://account.thethingsnetwork.org/applications"; + + 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"; + final String clientSecret = "your_client_secret"; + final String secretState = "secret" + new Random().nextInt(999_999); + final String redirectURI = "https://your_redirect_uri"; + + final OAuth20Service service = new ServiceBuilder(clientId) + .apiSecret(clientSecret) + .callback(redirectURI) + .build(TheThingsNetworkV1StagingApi.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(">>"); + + // 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(); + + 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("Oops, state value does not match!"); + System.out.println("Expected = " + secretState); + System.out.println("Got = " + value); + 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); + request.addHeader("Accept", "application/json"); + 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()); + + 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 new file mode 100644 index 000000000..25316d6ea --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/TheThingsNetworkV2PreviewExample.java @@ -0,0 +1,94 @@ +package com.github.scribejava.apis.examples; + +import com.github.scribejava.apis.TheThingsNetworkV2PreviewApi; +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 TheThingsNetworkV2PreviewExample { + + private static final String NETWORK_NAME = "TTNv2preview"; + 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"; + final String clientSecret = "your_client_secret"; + final String secretState = "secret" + new Random().nextInt(999_999); + final String redirectURI = "https://your_redirect_uri"; + + final OAuth20Service service = new ServiceBuilder(clientId) + .apiSecret(clientSecret) + .callback(redirectURI) + .build(TheThingsNetworkV2PreviewApi.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("Oops, state value does not match!"); + System.out.println("Expected = " + secretState); + System.out.println("Got = " + value); + 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); + request.addHeader("Accept", "application/json"); + 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()); + + 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 a72821cb2..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 @@ -10,16 +10,20 @@ import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth10aService; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract class TrelloExample { +public class TrelloExample { private static final String API_KEY = "your_api_key"; private static final String API_SECRET = "your_api_secret"; private static final String PROTECTED_RESOURCE_URL = "https://trello.com/1/members/me"; - public static void main(String... args) throws IOException { - final OAuth10aService service = new ServiceBuilder() - .apiKey(API_KEY) + 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) .build(TrelloApi.instance()); final Scanner in = new Scanner(System.in); @@ -40,23 +44,22 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - 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 10ae13e71..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 @@ -10,14 +10,18 @@ import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth10aService; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract class TumblrExample { +public class TumblrExample { private static final String PROTECTED_RESOURCE_URL = "http://api.tumblr.com/v2/user/info"; - public static void main(String... args) throws IOException { - final OAuth10aService service = new ServiceBuilder() - .apiKey("MY_CONSUMER_KEY") + 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") // OOB forbidden. We need an url and the better is on the tumblr website ! .callback("http://www.tumblr.com/connect/login_success.html") @@ -40,23 +44,22 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - 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 769270d37..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 @@ -11,18 +11,22 @@ import com.github.scribejava.core.model.OAuth2AccessToken; import com.github.scribejava.core.oauth.OAuth20Service; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract 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"; - public static void main(String... args) throws IOException { + 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"; final String clientSecret = "your client secret"; - final OAuth20Service service = new ServiceBuilder() - .apiKey(clientId) + final OAuth20Service service = new ServiceBuilder(clientId) .apiSecret(clientSecret) .callback("http://www.example.com/oauth_callback/") .build(TutByApi.instance()); @@ -41,24 +45,22 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); - + 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 66d8cefae..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 @@ -10,14 +10,18 @@ import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth10aService; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract class TwitterExample { +public class TwitterExample { private static final String PROTECTED_RESOURCE_URL = "https://api.twitter.com/1.1/account/verify_credentials.json"; - public static void main(String... args) throws IOException { - final OAuth10aService service = new ServiceBuilder() - .apiKey("your client id") + 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") .build(TwitterApi.instance()); final Scanner in = new Scanner(System.in); @@ -38,23 +42,22 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - 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/UcozExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/UcozExample.java new file mode 100644 index 000000000..5760bb36a --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/UcozExample.java @@ -0,0 +1,60 @@ +package com.github.scribejava.apis.examples; + +import com.github.scribejava.apis.UcozApi; +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 UcozExample { + + private static final String PROTECTED_RESOURCE_URL = "http://artmurka.com/uapi/shop/request?page=categories"; + + private UcozExample() { + } + + @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") + .debug() + .build(UcozApi.instance()); + final Scanner in = new Scanner(System.in); + 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.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 1a9887217..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 @@ -9,18 +9,22 @@ import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth20Service; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract 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"; - public static void main(String... args) throws IOException { + 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"; final String apiSecret = "your_api_secret"; - final OAuth20Service service = new ServiceBuilder() - .apiKey(apiKey) + final OAuth20Service service = new ServiceBuilder(apiKey) .apiSecret(apiSecret) .callback("http://www.example.com/oauth_callback/") .build(ViadeoApi.instance()); @@ -40,24 +44,22 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); - + 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/VkontakteAsyncNingExample.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteAsyncNingExample.java new file mode 100644 index 000000000..bfb80d1a9 --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteAsyncNingExample.java @@ -0,0 +1,95 @@ +package com.github.scribejava.apis.examples; + +import com.github.scribejava.httpclient.ning.NingHttpClientConfig; +import com.ning.http.client.AsyncHttpClientConfig; +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 java.io.IOException; + +public class VkontakteAsyncNingExample { + + 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 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"; + final String clientSecret = "your client secret"; + final String secretState = "secret" + new Random().nextInt(999_999); + final NingHttpClientConfig clientConfig = new NingHttpClientConfig(new AsyncHttpClientConfig.Builder() + .setMaxConnections(5) + .setRequestTimeout(10_000) + .setAllowPoolingConnections(false) + .setPooledConnectionIdleTimeout(1_000) + .setReadTimeout(1_000) + .build()); + + try ( OAuth20Service service = new ServiceBuilder(clientId) + .apiSecret(clientSecret) + .callback("http://www.example.com/oauth_callback/") + .httpClientConfig(clientConfig) + .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/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 5e50158bd..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,26 +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 abstract 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; - public static void main(String... args) throws IOException { + 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() - .apiKey(clientId) + 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); @@ -32,7 +39,10 @@ public static void main(String... args) throws IOException { // 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); @@ -41,24 +51,27 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); - + 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 new file mode 100644 index 000000000..fe5340294 --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/examples/VkontakteExternalHttpExample.java @@ -0,0 +1,87 @@ +package com.github.scribejava.apis.examples; + +import java.util.Scanner; +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.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.ahc.AhcHttpClient; +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import org.asynchttpclient.DefaultAsyncHttpClient; +import org.asynchttpclient.DefaultAsyncHttpClientConfig; + +public class VkontakteExternalHttpExample { + + 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"; + final String clientSecret = "your client secret"; + + //create any http client externally + final DefaultAsyncHttpClientConfig httpClientConfig = new DefaultAsyncHttpClientConfig.Builder() + .setMaxConnections(5) + .setRequestTimeout(10_000) + .setPooledConnectionIdleTimeout(1_000) + .setReadTimeout(1_000) + .build(); + //wrap it + try ( DefaultAsyncHttpClient ahcHttpClient = new DefaultAsyncHttpClient(httpClientConfig)) { + //wrap it + final AhcHttpClient wrappedAHCHttpClient = new AhcHttpClient(ahcHttpClient); + + final OAuth20Service service = new ServiceBuilder(clientId) + .httpClient(wrappedAHCHttpClient) + .apiSecret(clientSecret) + .defaultScope("wall,offline") // replace with desired scope + .callback("http://your.site.com/callback") + .build(VkontakteApi.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(); + + // 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/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 04a3668fd..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 @@ -10,14 +10,18 @@ import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth10aService; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract class XingExample { +public class XingExample { private static final String PROTECTED_RESOURCE_URL = "https://api.xing.com/v1/users/me"; - public static void main(String... args) throws IOException { - final OAuth10aService service = new ServiceBuilder() - .apiKey("your client id") + 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") .build(XingApi.instance()); final Scanner in = new Scanner(System.in); @@ -38,23 +42,22 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - 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 3d020d59d..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 @@ -10,15 +10,19 @@ import com.github.scribejava.core.model.Verb; import com.github.scribejava.core.oauth.OAuth10aService; import java.io.IOException; +import java.util.concurrent.ExecutionException; -public abstract class YahooExample { +public class YahooExample { private static final String PROTECTED_RESOURCE_URL = "http://social.yahooapis.com/v1/user/A6ROU63MXWDCW3Y5MGCYWVHDJI/profile/status?format=json"; - public static void main(String... args) throws IOException { - final OAuth10aService service = new ServiceBuilder() - .apiKey("your client id") + 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") .build(YahooApi.instance()); final Scanner in = new Scanner(System.in); @@ -39,24 +43,23 @@ public static void main(String... args) throws IOException { 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); + final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); service.signRequest(accessToken, request); - final Response response = request.send(); - System.out.println("Got it! Lets see what we found..."); - System.out.println(); - System.out.println(response.getCode()); - System.out.println(response.getBody()); - + 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/odnoklassniki/OdnoklassnikiServiceTest.java b/scribejava-apis/src/test/java/com/github/scribejava/apis/odnoklassniki/OdnoklassnikiServiceTest.java new file mode 100644 index 000000000..f4bfbccac --- /dev/null +++ b/scribejava-apis/src/test/java/com/github/scribejava/apis/odnoklassniki/OdnoklassnikiServiceTest.java @@ -0,0 +1,42 @@ +package com.github.scribejava.apis.odnoklassniki; + +import com.github.scribejava.apis.OdnoklassnikiApi; +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.ParameterList; +import com.github.scribejava.core.model.Verb; +import com.github.scribejava.core.model.Parameter; +import com.github.scribejava.core.oauth.OAuth20Service; +import static org.junit.Assert.assertEquals; +import org.junit.Test; + +public class OdnoklassnikiServiceTest { + + private static final String URL = "https://api.ok.ru/fb.do?method=friends.get&fields=uid%2C" + + "first_name%2Clast_name%2Cpic_2&application_key=AAAAAAAAAAAAAAAA&format=json"; + + private final OAuth20Service service = new ServiceBuilder("0000000000") + .apiSecret("CCCCCCCCCCCCCCCCCCCCCCCC") + .defaultScope("VALUABLE_ACCESS") + .callback("http://your.site.com/callback") + .build(OdnoklassnikiApi.instance()); + + @Test + public void testSigGeneration() { + final OAuth2AccessToken accessToken = new OAuth2AccessToken("d3iwa.403gvrs194740652m1k4w2a503k3c"); + final OAuthRequest request = new OAuthRequest(Verb.GET, URL); + service.signRequest(accessToken, request); + assertEquals("96127f5ca29a8351399e94bbd284ab16", findParam(request.getQueryStringParams(), "sig")); + } + + private static String findParam(ParameterList list, String key) { + for (Parameter param : list.getParams()) { + if (param.getKey().equals(key)) { + return param.getValue(); + } + } + return null; + } +} diff --git a/scribejava-core/pom.xml b/scribejava-core/pom.xml index 62e072945..d5fbfc488 100644 --- a/scribejava-core/pom.xml +++ b/scribejava-core/pom.xml @@ -5,15 +5,41 @@ com.github.scribejava scribejava - 2.8.2-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/async/ahc/OAuthAsyncCompletionHandler.java b/scribejava-core/src/main/java/com/github/scribejava/core/async/ahc/OAuthAsyncCompletionHandler.java deleted file mode 100644 index b556fb6ad..000000000 --- a/scribejava-core/src/main/java/com/github/scribejava/core/async/ahc/OAuthAsyncCompletionHandler.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.github.scribejava.core.async.ahc; - -import com.github.scribejava.core.model.OAuthAsyncRequestCallback; -import com.github.scribejava.core.model.OAuthRequestAsync; -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; -import org.asynchttpclient.AsyncCompletionHandler; - -public class OAuthAsyncCompletionHandler extends AsyncCompletionHandler { - - private final OAuthAsyncRequestCallback callback; - private final OAuthRequestAsync.ResponseConverter converter; - - public OAuthAsyncCompletionHandler(OAuthAsyncRequestCallback callback, - OAuthRequestAsync.ResponseConverter converter) { - this.callback = callback; - this.converter = converter; - } - - @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.getResponseBody(), ahcResponse.getResponseBodyAsStream()); - - @SuppressWarnings("unchecked") - final T t = converter == null ? (T) response : converter.convert(response); - if (callback != null) { - callback.onCompleted(t); - } - return t; - } - - @Override - public void onThrowable(Throwable t) { - if (callback != null) { - callback.onThrowable(t); - } - } -}; diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/async/ning/OAuthAsyncCompletionHandler.java b/scribejava-core/src/main/java/com/github/scribejava/core/async/ning/OAuthAsyncCompletionHandler.java deleted file mode 100644 index adbcf10bb..000000000 --- a/scribejava-core/src/main/java/com/github/scribejava/core/async/ning/OAuthAsyncCompletionHandler.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.github.scribejava.core.async.ning; - -import com.github.scribejava.core.model.OAuthAsyncRequestCallback; -import com.github.scribejava.core.model.OAuthRequestAsync; -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; -import java.util.Map; - -public class OAuthAsyncCompletionHandler extends AsyncCompletionHandler { - - private final OAuthAsyncRequestCallback callback; - private final OAuthRequestAsync.ResponseConverter converter; - - public OAuthAsyncCompletionHandler(OAuthAsyncRequestCallback callback, - OAuthRequestAsync.ResponseConverter converter) { - this.callback = callback; - this.converter = converter; - } - - @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); - } - headersMap.put(header.getKey(), value.toString()); - } - final Response response = new Response(ningResponse.getStatusCode(), ningResponse.getStatusText(), headersMap, - ningResponse.getResponseBody(), ningResponse.getResponseBodyAsStream()); - - @SuppressWarnings("unchecked") - final T t = converter == null ? (T) response : converter.convert(response); - if (callback != null) { - callback.onCompleted(t); - } - return t; - } - - @Override - public void onThrowable(Throwable t) { - if (callback != null) { - callback.onThrowable(t); - } - } -}; 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 12195ab56..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,190 +1,130 @@ package com.github.scribejava.core.builder; -import com.github.scribejava.core.builder.api.BaseApi; -import com.github.scribejava.core.model.OAuthConfig; -import java.io.OutputStream; -import com.github.scribejava.core.model.OAuthConstants; -import com.github.scribejava.core.model.SignatureType; +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.oauth.OAuth10aService; +import com.github.scribejava.core.oauth.OAuth20Service; import com.github.scribejava.core.oauth.OAuthService; import com.github.scribejava.core.utils.Preconditions; +import java.io.OutputStream; + /** * 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; private String apiKey; private String apiSecret; private String scope; - private String state; - private SignatureType signatureType; private OutputStream debugStream; private String responseType = "code"; private String userAgent; - //sync version only - private Integer connectTimeout; - private Integer readTimeout; + private HttpClientConfig httpClientConfig; + private HttpClient httpClient; - //async version only - //ning 1.9 - private com.ning.http.client.AsyncHttpClientConfig ningAsyncHttpClientConfig; - private String ningAsyncHttpProviderClassName; - //AHC 2.0 - private org.asynchttpclient.AsyncHttpClientConfig ahcAsyncHttpClientConfig; - - public ServiceBuilder() { - callback = OAuthConstants.OUT_OF_BAND; - signatureType = SignatureType.Header; + 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 - */ - public ServiceBuilder apiKey(String apiKey) { + @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); } - /** - * Configures the signature type, choose between header, querystring, etc. Defaults to Header - * - * @param signatureType SignatureType - * @return the {@link ServiceBuilder} instance for method chaining - */ - public ServiceBuilder signatureType(SignatureType signatureType) { - Preconditions.checkNotNull(signatureType, "Signature type can't be null"); - this.signatureType = signatureType; - return this; + @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; } - public ServiceBuilder connectTimeout(Integer connectTimeout) { - Preconditions.checkNotNull(connectTimeout, "Connection timeout can't be null"); - this.connectTimeout = connectTimeout; + @Override + public ServiceBuilder httpClientConfig(HttpClientConfig httpClientConfig) { + Preconditions.checkNotNull(httpClientConfig, "httpClientConfig can't be null"); + this.httpClientConfig = httpClientConfig; return this; } - public ServiceBuilder readTimeout(Integer readTimeout) { - Preconditions.checkNotNull(readTimeout, "Read timeout can't be null"); - this.readTimeout = readTimeout; - return this; - } - - public ServiceBuilder asyncNingHttpClientConfig(com.ning.http.client.AsyncHttpClientConfig asyncHttpClientConfig) { - Preconditions.checkNotNull(asyncHttpClientConfig, "asyncHttpClientConfig can't be null"); - ningAsyncHttpClientConfig = asyncHttpClientConfig; - ahcAsyncHttpClientConfig = null; - return this; - } - - public ServiceBuilder asyncNingHttpProviderClassName(String asyncHttpProviderClassName) { - this.ningAsyncHttpProviderClassName = asyncHttpProviderClassName; - ahcAsyncHttpClientConfig = null; - return this; - } - - public ServiceBuilder asyncAHCHttpClientConfig(org.asynchttpclient.AsyncHttpClientConfig asyncHttpClientConfig) { - Preconditions.checkNotNull(asyncHttpClientConfig, "asyncHttpClientConfig can't be null"); - ahcAsyncHttpClientConfig = asyncHttpClientConfig; - ningAsyncHttpClientConfig = null; - ningAsyncHttpProviderClassName = null; + @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; - } - - public void checkPreconditions() { - Preconditions.checkEmptyString(apiKey, "You must provide an api key"); + return debugStream(System.out); } - private OAuthConfig createConfig() { - checkPreconditions(); - return new OAuthConfig(apiKey, apiSecret, callback, signatureType, scope, debugStream, state, responseType, - userAgent, connectTimeout, readTimeout, ningAsyncHttpClientConfig, ningAsyncHttpProviderClassName, - ahcAsyncHttpClientConfig); + @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(createConfig()); + @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 a42fe35fa..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 3b4b148c6..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. @@ -77,6 +81,13 @@ public SignatureService getSignatureService() { return new HMACSha1SignatureService(); } + /** + * @return the signature type, choose between header, querystring, etc. Defaults to Header + */ + public OAuth1SignatureType getSignatureType() { + return OAuth1SignatureType.HEADER; + } + /** * Returns the timestamp service. * @@ -118,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 480a7a34d..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,8 +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 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 new file mode 100644 index 000000000..7de68ff7b --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/builder/api/OAuth1SignatureType.java @@ -0,0 +1,7 @@ +package com.github.scribejava.core.builder.api; + +public enum OAuth1SignatureType { + + HEADER, + QUERY_STRING +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/exceptions/OAuthConnectionException.java b/scribejava-core/src/main/java/com/github/scribejava/core/exceptions/OAuthConnectionException.java deleted file mode 100644 index 93feb8c42..000000000 --- a/scribejava-core/src/main/java/com/github/scribejava/core/exceptions/OAuthConnectionException.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.github.scribejava.core.exceptions; - -public class OAuthConnectionException extends OAuthException { - - private static final long serialVersionUID = 6901269342236961310L; - private static final String MSG = "There was a problem while creating a connection to the remote service: "; - - public OAuthConnectionException(String url, Exception e) { - super(MSG + url, e); - } -} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/exceptions/OAuthParametersMissingException.java b/scribejava-core/src/main/java/com/github/scribejava/core/exceptions/OAuthParametersMissingException.java index ddf77ab78..2d4a5dfc4 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/exceptions/OAuthParametersMissingException.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/exceptions/OAuthParametersMissingException.java @@ -1,6 +1,6 @@ package com.github.scribejava.core.exceptions; -import com.github.scribejava.core.model.AbstractRequest; +import com.github.scribejava.core.model.OAuthRequest; /** * Specialized exception that represents a missing OAuth parameter. @@ -16,7 +16,7 @@ public class OAuthParametersMissingException extends OAuthException { * * @param request OAuthRequest that caused the error */ - public OAuthParametersMissingException(AbstractRequest request) { + public OAuthParametersMissingException(OAuthRequest request) { super(String.format(MSG, 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/AbstractOAuth1TokenExtractor.java b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/AbstractOAuth1TokenExtractor.java index 4d968d83c..61578d01b 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/extractors/AbstractOAuth1TokenExtractor.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/AbstractOAuth1TokenExtractor.java @@ -1,9 +1,11 @@ package com.github.scribejava.core.extractors; +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.OAuth1Token; +import com.github.scribejava.core.model.Response; import com.github.scribejava.core.utils.OAuthEncoder; import com.github.scribejava.core.utils.Preconditions; @@ -16,19 +18,20 @@ */ public abstract class AbstractOAuth1TokenExtractor implements TokenExtractor { - private static final String OAUTH_TOKEN_REGEXP = "oauth_token=([^&]+)"; - private static final String OAUTH_TOKEN_SECRET_REGEXP = "oauth_token_secret=([^&]*)"; + private static final Pattern OAUTH_TOKEN_REGEXP_PATTERN = Pattern.compile("oauth_token=([^&]+)"); + private static final Pattern OAUTH_TOKEN_SECRET_REGEXP_PATTERN = Pattern.compile("oauth_token_secret=([^&]*)"); /** * {@inheritDoc} */ @Override - public T extract(String response) { - Preconditions.checkEmptyString(response, + public T 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"); - final String token = extract(response, Pattern.compile(OAUTH_TOKEN_REGEXP)); - final String secret = extract(response, Pattern.compile(OAUTH_TOKEN_SECRET_REGEXP)); - return createToken(token, secret, response); + final String token = extract(body, OAUTH_TOKEN_REGEXP_PATTERN); + final String secret = extract(body, OAUTH_TOKEN_SECRET_REGEXP_PATTERN); + return createToken(token, secret, body); } private String extract(String response, Pattern p) { diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/extractors/BaseStringExtractor.java b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/BaseStringExtractor.java index 113dc3515..21cb3431b 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/extractors/BaseStringExtractor.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/BaseStringExtractor.java @@ -1,19 +1,19 @@ package com.github.scribejava.core.extractors; -import com.github.scribejava.core.model.AbstractRequest; +import com.github.scribejava.core.model.OAuthRequest; /** - * Simple command object that extracts a base string from a {@link AbstractRequest} + * Simple command object that extracts a base string from a {@link OAuthRequest} */ public interface BaseStringExtractor { /** - * Extracts an url-encoded base string from the {@link AbstractRequest}. + * Extracts an url-encoded base string from the {@link OAuthRequest}. * * See the oauth spec for more info on this. * * @param request the OAuthRequest * @return the url-encoded base string */ - String extract(AbstractRequest request); + String extract(OAuthRequest request); } 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 e2ba3b950..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 @@ -1,13 +1,14 @@ package com.github.scribejava.core.extractors; import com.github.scribejava.core.exceptions.OAuthParametersMissingException; -import com.github.scribejava.core.model.AbstractRequest; +import com.github.scribejava.core.model.OAuthRequest; import com.github.scribejava.core.model.ParameterList; import com.github.scribejava.core.utils.OAuthEncoder; import com.github.scribejava.core.utils.Preconditions; /** * 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 { @@ -17,7 +18,7 @@ public class BaseStringExtractorImpl implements BaseStringExtractor { * {@inheritDoc} */ @Override - public String extract(AbstractRequest request) { + public String extract(OAuthRequest request) { checkPreconditions(request); final String verb = OAuthEncoder.encode(getVerb(request)); final String url = OAuthEncoder.encode(getUrl(request)); @@ -25,15 +26,20 @@ public String extract(AbstractRequest request) { return String.format(AMPERSAND_SEPARATED_STRING, verb, url, params); } - protected String getVerb(AbstractRequest request) { + protected String getVerb(OAuthRequest request) { return request.getVerb().name(); } - protected String getUrl(AbstractRequest request) { + /** + * https://tools.ietf.org/html/rfc5849#section-3.4.1.2 + * @param request request + * @return url + */ + protected String getUrl(OAuthRequest request) { return request.getSanitizedUrl(); } - protected String getSortedAndEncodedParams(AbstractRequest request) { + protected String getSortedAndEncodedParams(OAuthRequest request) { final ParameterList params = new ParameterList(); params.addAll(request.getQueryStringParams()); params.addAll(request.getBodyParams()); @@ -41,7 +47,7 @@ protected String getSortedAndEncodedParams(AbstractRequest request) { return params.sort().asOauthBaseString(); } - protected void checkPreconditions(AbstractRequest request) { + protected void checkPreconditions(OAuthRequest request) { Preconditions.checkNotNull(request, "Cannot extract base string from a null object"); if (request.getOauthParameters() == null || request.getOauthParameters().size() <= 0) { 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/HeaderExtractor.java b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/HeaderExtractor.java index d84390a91..e6e6156e4 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/extractors/HeaderExtractor.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/HeaderExtractor.java @@ -1,6 +1,6 @@ package com.github.scribejava.core.extractors; -import com.github.scribejava.core.model.AbstractRequest; +import com.github.scribejava.core.model.OAuthRequest; /** * Simple command object that generates an OAuth Authorization header to include in the request. @@ -13,5 +13,5 @@ public interface HeaderExtractor { * @param request the AbstractRequest to inspect and generate the header * @return the Http header value */ - String extract(AbstractRequest request); + String extract(OAuthRequest request); } 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 a3c142a10..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 @@ -3,7 +3,7 @@ import java.util.Map; import com.github.scribejava.core.exceptions.OAuthParametersMissingException; import com.github.scribejava.core.model.OAuthConstants; -import com.github.scribejava.core.model.AbstractRequest; +import com.github.scribejava.core.model.OAuthRequest; import com.github.scribejava.core.utils.OAuthEncoder; import com.github.scribejava.core.utils.Preconditions; @@ -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 "; @@ -20,27 +19,33 @@ public class HeaderExtractorImpl implements HeaderExtractor { * {@inheritDoc} */ @Override - public String extract(AbstractRequest request) { + 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(); } - private void checkPreconditions(AbstractRequest request) { + private void checkPreconditions(OAuthRequest request) { Preconditions.checkNotNull(request, "Cannot extract a header from a null object"); if (request.getOauthParameters() == null || request.getOauthParameters().size() <= 0) { 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 251226338..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 @@ -1,9 +1,12 @@ package com.github.scribejava.core.extractors; +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; import com.github.scribejava.core.utils.OAuthEncoder; import com.github.scribejava.core.utils.Preconditions; @@ -12,11 +15,11 @@ */ public class OAuth2AccessTokenExtractor implements TokenExtractor { - private static final String ACCESS_TOKEN_REGEX = "access_token=([^&]+)"; - private static final String TOKEN_TYPE_REGEX = "token_type=([^&]+)"; - private static final String EXPIRES_IN_REGEX = "expires_in=([^&]+)"; - private static final String REFRESH_TOKEN_REGEX = "refresh_token=([^&]+)"; - private static final String SCOPE_REGEX = "scope=([^&]+)"; + private static final Pattern ACCESS_TOKEN_REGEX_PATTERN = Pattern.compile("access_token=([^&]+)"); + private static final Pattern TOKEN_TYPE_REGEX_PATTERN = Pattern.compile("token_type=([^&]+)"); + private static final Pattern EXPIRES_IN_REGEX_PATTERN = Pattern.compile("expires_in=([^&]+)"); + private static final Pattern REFRESH_TOKEN_REGEX_PATTERN = Pattern.compile("refresh_token=([^&]+)"); + private static final Pattern SCOPE_REGEX_PATTERN = Pattern.compile("scope=([^&]+)"); protected OAuth2AccessTokenExtractor() { } @@ -34,31 +37,37 @@ public static OAuth2AccessTokenExtractor instance() { * {@inheritDoc} */ @Override - public OAuth2AccessToken extract(String response) { - Preconditions.checkEmptyString(response, + 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"); - final String accessToken = extractParameter(response, ACCESS_TOKEN_REGEX, true); - final String tokenType = extractParameter(response, TOKEN_TYPE_REGEX, false); - final String expiresInString = extractParameter(response, EXPIRES_IN_REGEX, false); + final String accessToken = extractParameter(body, ACCESS_TOKEN_REGEX_PATTERN, true); + final String tokenType = extractParameter(body, TOKEN_TYPE_REGEX_PATTERN, false); + final String expiresInString = extractParameter(body, EXPIRES_IN_REGEX_PATTERN, false); Integer expiresIn; try { expiresIn = expiresInString == null ? null : Integer.valueOf(expiresInString); } catch (NumberFormatException nfe) { expiresIn = null; } - final String refreshToken = extractParameter(response, REFRESH_TOKEN_REGEX, false); - final String scope = extractParameter(response, SCOPE_REGEX, false); + final String refreshToken = extractParameter(body, REFRESH_TOKEN_REGEX_PATTERN, false); + final String scope = extractParameter(body, SCOPE_REGEX_PATTERN, false); - return new OAuth2AccessToken(accessToken, tokenType, expiresIn, refreshToken, scope, response); + return new OAuth2AccessToken(accessToken, tokenType, expiresIn, refreshToken, scope, body); } - private static String extractParameter(String response, String regex, boolean required) throws OAuthException { - final Matcher matcher = Pattern.compile(regex).matcher(response); + private static String extractParameter(String response, Pattern regexPattern, boolean required) + throws OAuthException { + + final Matcher matcher = regexPattern.matcher(response); if (matcher.find()) { return OAuthEncoder.decode(matcher.group(1)); } else if (required) { - throw new OAuthException("Response body is incorrect. Can't extract a '" + regex + throw new OAuthException("Response body is incorrect. Can't extract a '" + regexPattern.pattern() + "' from this: '" + response + "'", null); } else { return null; 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 f6250f9b2..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,21 +1,20 @@ package com.github.scribejava.core.extractors; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import com.github.scribejava.core.exceptions.OAuthException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import java.io.IOException; +import java.net.URI; 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 String ACCESS_TOKEN_REGEX = "\"access_token\"\\s*:\\s*\"(\\S*?)\""; - private static final String TOKEN_TYPE_REGEX = "\"token_type\"\\s*:\\s*\"(\\S*?)\""; - private static final String EXPIRES_IN_REGEX = "\"expires_in\"\\s*:\\s*\"?(\\d*?)\"?\\D"; - private static final String REFRESH_TOKEN_REGEX = "\"refresh_token\"\\s*:\\s*\"(\\S*?)\""; - private static final String SCOPE_REGEX = "\"scope\"\\s*:\\s*\"(\\S*?)\""; +public class OAuth2AccessTokenJsonExtractor extends AbstractJsonExtractor implements TokenExtractor { protected OAuth2AccessTokenJsonExtractor() { } @@ -30,41 +29,72 @@ public static OAuth2AccessTokenJsonExtractor instance() { } @Override - public OAuth2AccessToken extract(String response) { - Preconditions.checkEmptyString(response, - "Response body is incorrect. Can't extract a token from an empty string"); - - final String accessToken = extractParameter(response, ACCESS_TOKEN_REGEX, true); - final String tokenType = extractParameter(response, TOKEN_TYPE_REGEX, false); - final String expiresInString = extractParameter(response, EXPIRES_IN_REGEX, false); - Integer expiresIn; - try { - expiresIn = expiresInString == null ? null : Integer.valueOf(expiresInString); - } catch (NumberFormatException nfe) { - expiresIn = null; - } - final String refreshToken = extractParameter(response, REFRESH_TOKEN_REGEX, false); - final String scope = extractParameter(response, SCOPE_REGEX, false); + 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"); - return createToken(accessToken, tokenType, expiresIn, refreshToken, scope, response); + if (response.getCode() != 200) { + generateError(response); + } + return createToken(body); } - protected OAuth2AccessToken createToken(String accessToken, String tokenType, Integer expiresIn, - String refreshToken, String scope, String response) { - return new OAuth2AccessToken(accessToken, tokenType, expiresIn, refreshToken, scope, response); - } + /** + * Related documentation: https://tools.ietf.org/html/rfc6749#section-5.2 + * + * @param response response + * @throws java.io.IOException IOException + * + */ + 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); + } - protected static String extractParameter(String response, String regex, boolean required) throws OAuthException { - final Matcher matcher = Pattern.compile(regex).matcher(response); - if (matcher.find()) { - return matcher.group(1); + final JsonNode errorUriInString = responseBodyJson.get("error_uri"); + URI errorUri; + try { + errorUri = errorUriInString == null ? null : URI.create(errorUriInString.asText()); + } catch (IllegalArgumentException iae) { + errorUri = null; } - if (required) { - throw new OAuthException("Response body is incorrect. Can't extract a '" + regex - + "' from this: '" + response + "'", null); + OAuth2Error errorCode; + try { + errorCode = OAuth2Error + .parseFrom(extractRequiredParameter(responseBodyJson, "error", responseBody).asText()); + } catch (IllegalArgumentException iaE) { + //non oauth standard error code + errorCode = null; } - return null; + final JsonNode errorDescription = responseBodyJson.get("error_description"); + + throw new OAuth2AccessTokenErrorResponse(errorCode, errorDescription == null ? null : errorDescription.asText(), + errorUri, response); + } + + private OAuth2AccessToken createToken(String rawResponse) throws IOException { + + 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 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/extractors/TokenExtractor.java b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/TokenExtractor.java index eec9546cc..00c662d81 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/extractors/TokenExtractor.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/extractors/TokenExtractor.java @@ -1,7 +1,11 @@ package com.github.scribejava.core.extractors; +import com.github.scribejava.core.exceptions.OAuthException; +import com.github.scribejava.core.model.Response; import com.github.scribejava.core.model.Token; +import java.io.IOException; + /** * Simple command object that extracts a concrete {@link Token} from a String * @param concrete type of Token @@ -11,8 +15,9 @@ public interface TokenExtractor { /** * Extracts the concrete type of token from the contents of an Http Response * - * @param response the contents of the response + * @param response the whole response * @return OAuth access token + * @throws java.io.IOException in case of troubles while getting body from the response */ - T extract(String response); + T extract(Response response) throws IOException, OAuthException; } 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 new file mode 100644 index 000000000..10b3d5faa --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/AbstractAsyncOnlyHttpClient.java @@ -0,0 +1,102 @@ +package com.github.scribejava.core.httpclient; + +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; +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +public abstract class AbstractAsyncOnlyHttpClient implements HttpClient { + + @Override + public Response execute(String userAgent, Map headers, Verb httpVerb, String completeUrl, + byte[] 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, + 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 { + + 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 { + + 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 new file mode 100644 index 000000000..821c9b194 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/HttpClient.java @@ -0,0 +1,45 @@ +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 extends Closeable { + + String DEFAULT_CONTENT_TYPE = "application/x-www-form-urlencoded"; + String CONTENT_TYPE = "Content-Type"; + String CONTENT_LENGTH = "Content-Length"; + + 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); + + Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, + File bodyContents, OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter); + + 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; + + Response execute(String userAgent, Map headers, Verb httpVerb, String completeUrl, + File bodyContents) throws InterruptedException, ExecutionException, IOException; +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/HttpClientConfig.java b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/HttpClientConfig.java new file mode 100644 index 000000000..3c28d6249 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/HttpClientConfig.java @@ -0,0 +1,6 @@ +package com.github.scribejava.core.httpclient; + +public interface HttpClientConfig { + + HttpClientConfig createDefaultConfig(); +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/HttpClientProvider.java b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/HttpClientProvider.java new file mode 100644 index 000000000..5b69156d2 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/HttpClientProvider.java @@ -0,0 +1,6 @@ +package com.github.scribejava.core.httpclient; + +public interface HttpClientProvider { + + HttpClient createClient(HttpClientConfig httpClientConfig); +} 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 new file mode 100644 index 000000000..b500ba997 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/jdk/JDKHttpClient.java @@ -0,0 +1,234 @@ +package com.github.scribejava.core.httpclient.jdk; + +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; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +public class JDKHttpClient implements HttpClient { + + private final JDKHttpClientConfig config; + + public JDKHttpClient() { + this(JDKHttpClientConfig.defaultConfig()); + } + + public JDKHttpClient(JDKHttpClientConfig clientConfig) { + config = clientConfig; + } + + @Override + public void close() { + } + + @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) { + + 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 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(t); + } + return new JDKHttpFuture<>(t); + } catch (IOException | RuntimeException e) { + if (callback != null) { + callback.onThrowable(e); + } + return new JDKHttpFuture<>(e); + } + } + + @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 multipartPayloads) throws InterruptedException, ExecutionException, IOException { + return doExecute(userAgent, headers, httpVerb, completeUrl, BodyType.MULTIPART, multipartPayloads); + } + + @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 { + 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 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) { + connection.setConnectTimeout(config.getConnectTimeout()); + } + if (config.getReadTimeout() != null) { + connection.setReadTimeout(config.getReadTimeout()); + } + addHeaders(connection, headers, userAgent); + if (httpVerb.isPermitBody()) { + bodyType.setBody(connection, bodyContents, httpVerb.isRequiresBody()); + } + + try { + connection.connect(); + final int responseCode = connection.getResponseCode(); + return new Response(responseCode, connection.getResponseMessage(), parseHeaders(connection), + responseCode >= 200 && responseCode < 400 ? connection.getInputStream() + : connection.getErrorStream()); + } catch (UnknownHostException e) { + throw new OAuthException("The IP address of a host could not be determined.", e); + } + } + + private enum BodyType { + BYTE_ARRAY { + @Override + void setBody(HttpURLConnection connection, Object bodyContents, boolean requiresBody) throws IOException { + 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 { + addBody(connection, ((String) bodyContents).getBytes(), requiresBody); + } + }; + + abstract void setBody(HttpURLConnection connection, Object bodyContents, boolean requiresBody) + throws IOException; + } + + private static Map parseHeaders(HttpURLConnection conn) { + final Map headers = new HashMap<>(); + + 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", value); + } else { + headers.put(key, value); + } + } + return headers; + } + + private static void addHeaders(HttpURLConnection connection, Map headers, String userAgent) { + for (Map.Entry header : headers.entrySet()) { + connection.setRequestProperty(header.getKey(), header.getValue()); + } + + if (userAgent != null) { + connection.setRequestProperty(OAuthConstants.USER_AGENT_HEADER_NAME, userAgent); + } + } + + private static void addBody(HttpURLConnection connection, byte[] content, boolean requiresBody) throws IOException { + final int contentLength = content.length; + if (requiresBody || contentLength > 0) { + 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 (requiresBody) { + final ByteArrayOutputStream os = MultipartUtils.getPayload(multipartPayload); + final int contentLength = os.size(); + final OutputStream outputStream = prepareConnectionForBodyAndGetOutputStream(connection, contentLength); + if (contentLength > 0) { + os.writeTo(outputStream); + } + } + } + + 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 new file mode 100644 index 000000000..3b322b0af --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/jdk/JDKHttpClientConfig.java @@ -0,0 +1,93 @@ +package com.github.scribejava.core.httpclient.jdk; + +import java.net.Proxy; + +import com.github.scribejava.core.httpclient.HttpClientConfig; + +public class JDKHttpClientConfig implements HttpClientConfig { + + private Integer connectTimeout; + private Integer readTimeout; + private boolean followRedirects = true; + private Proxy proxy; + + @Override + public JDKHttpClientConfig createDefaultConfig() { + return defaultConfig(); + } + + public static JDKHttpClientConfig defaultConfig() { + return new JDKHttpClientConfig(); + } + + public Integer getConnectTimeout() { + return connectTimeout; + } + + public void setConnectTimeout(Integer connectTimeout) { + this.connectTimeout = connectTimeout; + } + + public JDKHttpClientConfig withConnectTimeout(Integer connectTimeout) { + this.connectTimeout = connectTimeout; + return this; + } + + public Integer getReadTimeout() { + return readTimeout; + } + + 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; + } + + /** + * 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 + */ + 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 new file mode 100644 index 000000000..c4443cc87 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/jdk/JDKHttpFuture.java @@ -0,0 +1,60 @@ +package com.github.scribejava.core.httpclient.jdk; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * 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 { + + private final Exception exception; + private final V response; + + public JDKHttpFuture(Exception exception) { + this(null, exception); + } + + public JDKHttpFuture(V response) { + this(response, null); + } + + private JDKHttpFuture(V response, Exception exception) { + this.response = response; + this.exception = exception; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return true; + } + + @Override + public V get() throws InterruptedException, ExecutionException { + if (exception != null) { + throw new ExecutionException(exception); + } + + return response; + } + + @Override + public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + return get(); + } +} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/jdk/JDKHttpProvider.java b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/jdk/JDKHttpProvider.java new file mode 100644 index 000000000..04472e78a --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/httpclient/jdk/JDKHttpProvider.java @@ -0,0 +1,16 @@ +package com.github.scribejava.core.httpclient.jdk; + +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; +import com.github.scribejava.core.httpclient.HttpClientProvider; + +public class JDKHttpProvider implements HttpClientProvider { + + @Override + public HttpClient createClient(HttpClientConfig config) { + if (config instanceof JDKHttpClientConfig) { + return new JDKHttpClient((JDKHttpClientConfig) config); + } + return null; + } +} 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/AbstractRequest.java b/scribejava-core/src/main/java/com/github/scribejava/core/model/AbstractRequest.java deleted file mode 100644 index 4db6ccec7..000000000 --- a/scribejava-core/src/main/java/com/github/scribejava/core/model/AbstractRequest.java +++ /dev/null @@ -1,294 +0,0 @@ -package com.github.scribejava.core.model; - -import java.io.UnsupportedEncodingException; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.charset.Charset; -import java.util.HashMap; -import java.util.Map; -import com.github.scribejava.core.exceptions.OAuthException; -import com.github.scribejava.core.oauth.OAuthService; - -/** - * The representation of an OAuth HttpRequest. - */ -public abstract class AbstractRequest { - - public static final String DEFAULT_CONTENT_TYPE = "application/x-www-form-urlencoded"; - public static final String CONTENT_TYPE = "Content-Type"; - protected static final String CONTENT_LENGTH = "Content-Length"; - private static final String OAUTH_PREFIX = "oauth_"; - - private final String url; - private final Verb verb; - private final ParameterList querystringParams = new ParameterList(); - private final ParameterList bodyParams = new ParameterList(); - private final Map headers = new HashMap<>(); - private boolean connectionKeepAlive; - private boolean followRedirects = true; - private final OAuthService service; - - private String payload; - private String charset; - private byte[] bytePayload; - private final Map oauthParameters = new HashMap<>(); - - private String realm; - - /** - * Default constructor. - * - * @param verb Http verb/method - * @param url resource URL - * @param service OAuthService - */ - public AbstractRequest(Verb verb, String url, OAuthService service) { - this.verb = verb; - this.url = url; - this.service = service; - } - - /** - * Adds an OAuth parameter. - * - * @param key name of the parameter - * @param value value of the parameter - * @throws IllegalArgumentException if the parameter is not an OAuth parameter - */ - public void addOAuthParameter(String key, String value) { - oauthParameters.put(checkKey(key), value); - } - - private String checkKey(String key) { - if (key.startsWith(OAUTH_PREFIX) || key.equals(OAuthConstants.SCOPE) || key.equals(OAuthConstants.REALM)) { - return key; - } else { - throw new IllegalArgumentException( - String.format("OAuth parameters must either be '%s', '%s' or start with '%s'", OAuthConstants.SCOPE, - OAuthConstants.REALM, OAUTH_PREFIX)); - } - } - - public Map getOauthParameters() { - return oauthParameters; - } - - public void setRealm(String realm) { - this.realm = realm; - } - - public String getRealm() { - return realm; - } - - /** - * Returns the complete url (host + resource + encoded querystring parameters). - * - * @return the complete url. - */ - public String getCompleteUrl() { - return querystringParams.appendTo(url); - } - - /** - * Add an HTTP Header to the Request - * - * @param key the header name - * @param value the header value - */ - public void addHeader(String key, String value) { - this.headers.put(key, value); - } - - /** - * Add a body Parameter (for POST/ PUT Requests) - * - * @param key the parameter name - * @param value the parameter value - */ - public void addBodyParameter(String key, String value) { - this.bodyParams.add(key, value); - } - - /** - * Add a QueryString parameter - * - * @param key the parameter name - * @param value the parameter value - */ - public void addQuerystringParameter(String key, String value) { - this.querystringParams.add(key, value); - } - - public void addParameter(String key, String value) { - if (hasBodyContent()) { - bodyParams.add(key, value); - } else { - querystringParams.add(key, value); - } - } - - protected boolean hasBodyContent() { - return verb == Verb.PUT || verb == Verb.POST; - } - - /** - * Add 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 - * - * @param payload the body of the request - */ - public void addPayload(String payload) { - this.payload = payload; - } - - /** - * Overloaded version for byte arrays - * - * @param payload byte[] - */ - public void addPayload(byte[] payload) { - this.bytePayload = payload.clone(); - } - - /** - * Get a {@link ParameterList} with the query string parameters. - * - * @return a {@link ParameterList} containing the query string parameters. - * @throws OAuthException if the request URL is not valid. - */ - public ParameterList getQueryStringParams() { - try { - final ParameterList result = new ParameterList(); - final String queryString = new URL(url).getQuery(); - result.addQuerystring(queryString); - result.addAll(querystringParams); - return result; - } catch (MalformedURLException mue) { - throw new OAuthException("Malformed URL", mue); - } - } - - /** - * Obtains a {@link ParameterList} of the body parameters. - * - * @return a {@link ParameterList}containing the body parameters. - */ - public ParameterList getBodyParams() { - return bodyParams; - } - - /** - * Obtains the URL of the HTTP Request. - * - * @return the original URL of the HTTP Request - */ - public String getUrl() { - return url; - } - - /** - * Returns the URL without the port and the query string part. - * - * @return the OAuth-sanitized URL - */ - public String getSanitizedUrl() { - if (url.startsWith("http://") && (url.endsWith(":80") || url.contains(":80/"))) { - return url.replaceAll("\\?.*", "").replaceAll(":80", ""); - } else if (url.startsWith("https://") && (url.endsWith(":443") || url.contains(":443/"))) { - return url.replaceAll("\\?.*", "").replaceAll(":443", ""); - } else { - return url.replaceAll("\\?.*", ""); - } - } - - /** - * Returns the body of the request - * - * @return form encoded string - * - * @throws OAuthException if the charset chosen is not supported - */ - public String getBodyContents() { - try { - return new String(getByteBodyContents(), getCharset()); - } catch (UnsupportedEncodingException uee) { - throw new OAuthException("Unsupported Charset: " + charset, uee); - } - } - - byte[] getByteBodyContents() { - if (bytePayload != null) { - return bytePayload; - } - final String body = (payload == null) ? bodyParams.asFormUrlEncodedString() : payload; - try { - return body.getBytes(getCharset()); - } catch (UnsupportedEncodingException uee) { - throw new OAuthException("Unsupported Charset: " + getCharset(), uee); - } - } - - @Override - public String toString() { - return String.format("@Request(%s %s)", getVerb(), getUrl()); - } - - public Verb getVerb() { - return verb; - } - - public Map getHeaders() { - return headers; - } - - public String getCharset() { - return charset == null ? Charset.defaultCharset().name() : charset; - } - - /** - * Set the charset of the body of the request - * - * @param charsetName name of the charset of the request - */ - public void setCharset(String charsetName) { - charset = charsetName; - } - - /** - * Sets whether the underlying Http Connection is persistent or not. - * - * @param connectionKeepAlive boolean - * - * @see http://download.oracle.com/javase/1.5.0/docs/guide/net/http-keepalive.html - */ - public void setConnectionKeepAlive(boolean connectionKeepAlive) { - this.connectionKeepAlive = connectionKeepAlive; - } - - /** - * 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 - */ - public void setFollowRedirects(boolean followRedirects) { - this.followRedirects = followRedirects; - } - - public boolean isConnectionKeepAlive() { - return connectionKeepAlive; - } - - public boolean isFollowRedirects() { - return followRedirects; - } - - public OAuthService getService() { - return service; - } -} 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/ForceTypeOfHttpRequest.java b/scribejava-core/src/main/java/com/github/scribejava/core/model/ForceTypeOfHttpRequest.java deleted file mode 100644 index b0387a535..000000000 --- a/scribejava-core/src/main/java/com/github/scribejava/core/model/ForceTypeOfHttpRequest.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.github.scribejava.core.model; - -public enum ForceTypeOfHttpRequest { - - NONE, - FORCE_ASYNC_ONLY_HTTP_REQUESTS, - FORCE_SYNC_ONLY_HTTP_REQUESTS, - PREFER_ASYNC_ONLY_HTTP_REQUESTS, - PREFER_SYNC_ONLY_HTTP_REQUESTS -} 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 new file mode 100644 index 000000000..5c4a65a19 --- /dev/null +++ b/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuth2AccessTokenErrorResponse.java @@ -0,0 +1,39 @@ +package com.github.scribejava.core.model; + +import com.github.scribejava.core.oauth2.OAuth2Error; +import java.io.IOException; + +import java.net.URI; + +/** + * Representing "5.2. Error Response" + */ +public class OAuth2AccessTokenErrorResponse extends OAuthResponseException { + + private static final long serialVersionUID = 2309424849700276816L; + + private final OAuth2Error error; + private final String errorDescription; + private final URI errorUri; + + public OAuth2AccessTokenErrorResponse(OAuth2Error error, String errorDescription, URI errorUri, + Response rawResponse) throws IOException { + super(rawResponse); + this.error = error; + this.errorDescription = errorDescription; + this.errorUri = errorUri; + } + + public OAuth2Error getError() { + return error; + } + + public String getErrorDescription() { + return errorDescription; + } + + public URI getErrorUri() { + return errorUri; + } + +} 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 d5bba4899..000000000 --- a/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuthConfig.java +++ /dev/null @@ -1,118 +0,0 @@ -package com.github.scribejava.core.model; - -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 SignatureType signatureType; - private final String scope; - private final OutputStream debugStream; - private final String state; - private final String responseType; - private final String userAgent; - - //sync only version - private final Integer connectTimeout; - private final Integer readTimeout; - - //async version only - //ning 1.9 - private com.ning.http.client.AsyncHttpClientConfig ningAsyncHttpClientConfig; - private String ningAsyncHttpProviderClassName; - //AHC 2.0 - private org.asynchttpclient.AsyncHttpClientConfig ahcAsyncHttpClientConfig; - - public OAuthConfig(String key, String secret) { - this(key, secret, null, null, null, null, null, null, null, null, null, null, null, null); - } - - public OAuthConfig(String apiKey, String apiSecret, String callback, SignatureType signatureType, String scope, - OutputStream debugStream, String state, String responseType, String userAgent, Integer connectTimeout, - Integer readTimeout, com.ning.http.client.AsyncHttpClientConfig ningAsyncHttpClientConfig, - String ningAsyncHttpProviderClassName, org.asynchttpclient.AsyncHttpClientConfig ahcAsyncHttpClientConfig) { - this.apiKey = apiKey; - this.apiSecret = apiSecret; - this.callback = callback; - this.signatureType = signatureType; - this.scope = scope; - this.debugStream = debugStream; - this.state = state; - this.responseType = responseType; - this.userAgent = userAgent; - this.connectTimeout = connectTimeout; - this.readTimeout = readTimeout; - this.ningAsyncHttpClientConfig = ningAsyncHttpClientConfig; - this.ningAsyncHttpProviderClassName = ningAsyncHttpProviderClassName; - this.ahcAsyncHttpClientConfig = ahcAsyncHttpClientConfig; - } - - public String getApiKey() { - return apiKey; - } - - public String getApiSecret() { - return apiSecret; - } - - public String getCallback() { - return callback; - } - - public SignatureType getSignatureType() { - return signatureType; - } - - 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 Integer getConnectTimeout() { - return connectTimeout; - } - - public Integer getReadTimeout() { - return readTimeout; - } - - public com.ning.http.client.AsyncHttpClientConfig getNingAsyncHttpClientConfig() { - return ningAsyncHttpClientConfig; - } - - public String getNingAsyncHttpProviderClassName() { - return ningAsyncHttpProviderClassName; - } - - public org.asynchttpclient.AsyncHttpClientConfig getAhcAsyncHttpClientConfig() { - return ahcAsyncHttpClientConfig; - } -} 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 230cd15b9..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,92 +1,330 @@ 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.net.HttpURLConnection; +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; import java.net.URL; +import java.nio.charset.Charset; +import java.util.HashMap; import java.util.Map; -import com.github.scribejava.core.exceptions.OAuthConnectionException; -import com.github.scribejava.core.exceptions.OAuthException; -import com.github.scribejava.core.oauth.OAuthService; +import java.util.TreeMap; -public class OAuthRequest extends AbstractRequest { +/** + * The representation of an OAuth HttpRequest. + */ +public class OAuthRequest { - private HttpURLConnection connection; + private static final String OAUTH_PREFIX = "oauth_"; - public OAuthRequest(Verb verb, String url, OAuthService service) { - super(verb, url, service); - } + private final String url; + private final Verb verb; + private final ParameterList querystringParams = new ParameterList(); + private final ParameterList bodyParams = new ParameterList(); + 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<>(); + + private String realm; /** - * Execute the request and return a {@link Response} + * Default constructor. * - * @return Http Response + * @param verb Http verb/method + * @param url resource URL + */ + public OAuthRequest(Verb verb, String url) { + this.verb = verb; + this.url = url; + } + + /** + * Adds an OAuth parameter. * - * @throws RuntimeException if the connection cannot be created. + * @param key name of the parameter + * @param value value of the parameter + * @throws IllegalArgumentException if the parameter is not an OAuth parameter */ - public Response send() { - final ForceTypeOfHttpRequest forceTypeOfHttpRequest = ScribeJavaConfig.getForceTypeOfHttpRequests(); + public void addOAuthParameter(String key, String value) { + oauthParameters.put(checkKey(key), value); + } - if (ForceTypeOfHttpRequest.FORCE_ASYNC_ONLY_HTTP_REQUESTS == forceTypeOfHttpRequest) { - throw new OAuthException("Cannot use sync operations, only async"); + private String checkKey(String key) { + if (key.startsWith(OAUTH_PREFIX) || key.equals(OAuthConstants.SCOPE) || key.equals(OAuthConstants.REALM)) { + return key; + } else { + throw new IllegalArgumentException( + String.format("OAuth parameters must either be '%s', '%s' or start with '%s'", OAuthConstants.SCOPE, + OAuthConstants.REALM, OAUTH_PREFIX)); } - if (ForceTypeOfHttpRequest.PREFER_ASYNC_ONLY_HTTP_REQUESTS == forceTypeOfHttpRequest) { - getService().getConfig().log("Cannot use sync operations, only async"); + } + + public Map getOauthParameters() { + return oauthParameters; + } + + public void setRealm(String realm) { + this.realm = realm; + } + + public String getRealm() { + return realm; + } + + /** + * Returns the complete url (host + resource + encoded querystring parameters). + * + * @return the complete url. + */ + public String getCompleteUrl() { + return querystringParams.appendTo(url); + } + + /** + * Add an HTTP Header to the Request + * + * @param key the header name + * @param value the header value + */ + public void addHeader(String key, String value) { + headers.put(key, value); + } + + /** + * Add a body Parameter (for POST/ PUT Requests) + * + * @param key the parameter name + * @param value the parameter value + */ + public void addBodyParameter(String key, String value) { + bodyParams.add(key, value); + } + + /** + * Add a QueryString parameter + * + * @param key the parameter name + * @param value the parameter value + */ + public void addQuerystringParameter(String key, String value) { + querystringParams.add(key, value); + } + + public void addParameter(String key, String value) { + if (verb.isPermitBody()) { + bodyParams.add(key, value); + } else { + querystringParams.add(key, 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 + * + * @param payload the body of the request + */ + public void setPayload(String payload) { + resetPayload(); + stringPayload = payload; + } + + /** + * Overloaded version for byte arrays + * + * @param payload byte[] + */ + public void setPayload(byte[] payload) { + resetPayload(); + byteArrayPayload = payload.clone(); + } + + /** + * Overloaded version for File + * + * @param payload File + */ + public void setPayload(File payload) { + resetPayload(); + filePayload = payload; + } + + private void resetPayload() { + stringPayload = null; + byteArrayPayload = null; + filePayload = null; + multipartPayload = null; + } + + /** + * Get a {@link ParameterList} with the query string parameters. + * + * @return a {@link ParameterList} containing the query string parameters. + * @throws OAuthException if the request URL is not valid. + */ + public ParameterList getQueryStringParams() { try { - createConnection(); - return doSend(); - } catch (IOException | RuntimeException e) { - throw new OAuthConnectionException(getCompleteUrl(), e); + final ParameterList result = new ParameterList(); + final String queryString = new URL(url).getQuery(); + result.addQuerystring(queryString); + result.addAll(querystringParams); + return result; + } catch (MalformedURLException mue) { + throw new OAuthException("Malformed URL", mue); } } - Response doSend() throws IOException { - final Verb verb = getVerb(); - connection.setRequestMethod(verb.name()); - final OAuthConfig config = getService().getConfig(); - if (config.getConnectTimeout() != null) { - connection.setConnectTimeout(config.getConnectTimeout()); - } - if (config.getReadTimeout() != null) { - connection.setReadTimeout(config.getReadTimeout()); - } - addHeaders(); - if (hasBodyContent()) { - addBody(getByteBodyContents()); - } - return new Response(connection); + /** + * Obtains a {@link ParameterList} of the body parameters. + * + * @return a {@link ParameterList}containing the body parameters. + */ + public ParameterList getBodyParams() { + return bodyParams; } - private void createConnection() throws IOException { - final String completeUrl = getCompleteUrl(); - if (connection == null) { - System.setProperty("http.keepAlive", isConnectionKeepAlive() ? "true" : "false"); - connection = (HttpURLConnection) new URL(completeUrl).openConnection(); - connection.setInstanceFollowRedirects(isFollowRedirects()); + /** + * Obtains the URL of the HTTP Request. + * + * @return the original URL of the HTTP Request + */ + public String getUrl() { + return url; + } + + /** + * Returns the URL without the port and the query string part. + * + * @return the OAuth-sanitized URL + */ + public String getSanitizedUrl() { + if (url.startsWith("http://") && (url.endsWith(":80") || url.contains(":80/"))) { + return url.replaceAll("\\?.*", "").replaceAll(":80", ""); + } else if (url.startsWith("https://") && (url.endsWith(":443") || url.contains(":443/"))) { + return url.replaceAll("\\?.*", "").replaceAll(":443", ""); + } else { + return url.replaceAll("\\?.*", ""); } } - void addHeaders() { - for (Map.Entry entry : getHeaders().entrySet()) { - connection.setRequestProperty(entry.getKey(), entry.getValue()); + /** + * Returns the body of the request (set in {@link #setPayload(java.lang.String)}) + * + * @return form encoded string + */ + public String getStringPayload() { + return stringPayload; + } + + /** + * @return the body of the request (set in {@link #setPayload(byte[])} or in + * {@link #addBodyParameter(java.lang.String, java.lang.String)} ) + */ + public byte[] getByteArrayPayload() { + if (byteArrayPayload != null) { + return byteArrayPayload; } - final String userAgent = getService().getConfig().getUserAgent(); - if (userAgent != null) { - connection.setRequestProperty(OAuthConstants.USER_AGENT_HEADER_NAME, userAgent); + final String body = bodyParams.asFormUrlEncodedString(); + try { + return body.getBytes(getCharset()); + } catch (UnsupportedEncodingException uee) { + throw new OAuthException("Unsupported Charset: " + getCharset(), uee); } } - void addBody(byte[] content) throws IOException { - connection.setRequestProperty(CONTENT_LENGTH, String.valueOf(content.length)); + public File getFilePayload() { + return filePayload; + } - if (connection.getRequestProperty(CONTENT_TYPE) == null) { - connection.setRequestProperty(CONTENT_TYPE, DEFAULT_CONTENT_TYPE); - } - connection.setDoOutput(true); - connection.getOutputStream().write(content); + @Override + public String toString() { + return String.format("@Request(%s %s)", getVerb(), getUrl()); + } + + public Verb getVerb() { + return verb; + } + + public Map getHeaders() { + return headers; + } + + public String getCharset() { + return charset == null ? Charset.defaultCharset().name() : charset; } - void setConnection(HttpURLConnection connection) { - this.connection = connection; + /** + * Set the charset of the body of the request + * + * @param charsetName name of the charset of the request + */ + public void setCharset(String charsetName) { + charset = 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/OAuthRequestAsync.java b/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuthRequestAsync.java deleted file mode 100644 index 1a92f0699..000000000 --- a/scribejava-core/src/main/java/com/github/scribejava/core/model/OAuthRequestAsync.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.github.scribejava.core.model; - -import com.github.scribejava.core.exceptions.OAuthException; -import com.github.scribejava.core.oauth.OAuthService; - -import java.io.IOException; -import java.util.concurrent.Future; - -public class OAuthRequestAsync extends AbstractRequest { - - public OAuthRequestAsync(Verb verb, String url, OAuthService service) { - super(verb, url, service); - } - - public Future sendAsync(OAuthAsyncRequestCallback callback, ResponseConverter converter) { - final ForceTypeOfHttpRequest forceTypeOfHttpRequest = ScribeJavaConfig.getForceTypeOfHttpRequests(); - if (ForceTypeOfHttpRequest.FORCE_SYNC_ONLY_HTTP_REQUESTS == forceTypeOfHttpRequest) { - throw new OAuthException("Cannot use async operations, only sync"); - } - final OAuthService service = getService(); - final OAuthConfig config = service.getConfig(); - if (ForceTypeOfHttpRequest.PREFER_SYNC_ONLY_HTTP_REQUESTS == forceTypeOfHttpRequest) { - config.log("Cannot use async operations, only sync"); - } - return service.executeAsync(getHeaders(), getVerb(), getCompleteUrl(), getBodyContents(), callback, converter); - } - - public Future sendAsync(OAuthAsyncRequestCallback callback) { - return sendAsync(callback, null); - } - - public interface ResponseConverter { - - 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 869066788..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 @@ -2,62 +2,67 @@ import java.io.IOException; import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.UnknownHostException; -import java.util.HashMap; import java.util.Map; -import com.github.scribejava.core.exceptions.OAuthException; 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 int code; - private String message; + private final int code; + private final String message; + private final Map headers; private String body; private InputStream stream; - private Map headers; + private Closeable[] closeables; + private boolean closed; - public Response(int code, String message, Map headers, String body, InputStream stream) { + private Response(int code, String message, Map headers) { this.code = code; - this.headers = headers; - this.body = body; this.message = message; + this.headers = headers; + } + + public Response(int code, String message, Map headers, InputStream stream, + Closeable... closeables) { + this(code, message, headers); this.stream = stream; + this.closeables = closeables; } - Response(HttpURLConnection connection) throws IOException { - try { - connection.connect(); - code = connection.getResponseCode(); - message = connection.getResponseMessage(); - headers = parseHeaders(connection); - stream = isSuccessful() ? connection.getInputStream() : connection.getErrorStream(); - } catch (UnknownHostException e) { - throw new OAuthException("The IP address of a host could not be determined.", e); - } + public Response(int code, String message, Map headers, String body) { + this(code, message, headers); + this.body = body; } private String parseBodyContents() throws IOException { + if (stream == null) { + return null; + } if ("gzip".equals(getHeader("Content-Encoding"))) { - body = StreamUtils.getGzipStreamContents(getStream()); + body = StreamUtils.getGzipStreamContents(stream); } else { - body = StreamUtils.getStreamContents(getStream()); + body = StreamUtils.getStreamContents(stream); } return body; } - private Map parseHeaders(HttpURLConnection conn) { - final Map headers = new HashMap<>(); - for (String key : conn.getHeaderFields().keySet()) { - headers.put(key, conn.getHeaderFields().get(key).get(0)); - } - return headers; - } - - public final boolean isSuccessful() { - return getCode() >= 200 && getCode() < 400; + 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; } @@ -77,7 +82,7 @@ public InputStream getStream() { * * @return the status code */ - public final int getCode() { + public int getCode() { return code; } @@ -113,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/model/ScribeJavaConfig.java b/scribejava-core/src/main/java/com/github/scribejava/core/model/ScribeJavaConfig.java deleted file mode 100644 index 171bbac1a..000000000 --- a/scribejava-core/src/main/java/com/github/scribejava/core/model/ScribeJavaConfig.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.github.scribejava.core.model; - -public abstract class ScribeJavaConfig { - - private static ForceTypeOfHttpRequest forceTypeOfHttpRequests = ForceTypeOfHttpRequest.NONE; - - public static ForceTypeOfHttpRequest getForceTypeOfHttpRequests() { - return forceTypeOfHttpRequests; - } - - public static void setForceTypeOfHttpRequests(ForceTypeOfHttpRequest forceTypeOfHttpRequests) { - ScribeJavaConfig.forceTypeOfHttpRequests = forceTypeOfHttpRequests; - } -} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/model/SignatureType.java b/scribejava-core/src/main/java/com/github/scribejava/core/model/SignatureType.java deleted file mode 100644 index 26ffe054e..000000000 --- a/scribejava-core/src/main/java/com/github/scribejava/core/model/SignatureType.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.github.scribejava.core.model; - -public enum SignatureType { - - Header, - QueryString -} diff --git a/scribejava-core/src/main/java/com/github/scribejava/core/model/Verb.java b/scribejava-core/src/main/java/com/github/scribejava/core/model/Verb.java index 6ca83a517..8df5fd3f3 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/model/Verb.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/model/Verb.java @@ -5,5 +5,28 @@ */ public enum Verb { - GET, POST, PUT, DELETE, HEAD, OPTIONS, TRACE, PATCH + GET(false), POST(true), PUT(true), DELETE(false, true), HEAD(false), OPTIONS(false), TRACE(false), PATCH(true); + + private final boolean requiresBody; + private final boolean permitBody; + + Verb(boolean requiresBody) { + this(requiresBody, requiresBody); + } + + Verb(boolean requiresBody, boolean permitBody) { + if (requiresBody && !permitBody) { + throw new IllegalArgumentException(); + } + this.requiresBody = requiresBody; + this.permitBody = permitBody; + } + + public boolean isRequiresBody() { + return requiresBody; + } + + public boolean isPermitBody() { + return permitBody; + } } 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 e707486bc..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,20 +1,20 @@ 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.model.AbstractRequest; +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.OAuthRequestAsync; 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} @@ -23,125 +23,145 @@ 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 { - final OAuthConfig config = getConfig(); - config.log("obtaining request token from " + api.getRequestTokenEndpoint()); - final OAuthRequest request = new OAuthRequest(api.getRequestTokenVerb(), api.getRequestTokenEndpoint(), this); - - prepareRequestTokenRequest(request); - - config.log("sending request..."); - final Response response = request.send(); - final String body = response.getBody(); + public OAuth1RequestToken getRequestToken() throws IOException, InterruptedException, ExecutionException { + if (isDebug()) { + log("obtaining request token from %s", api.getRequestTokenEndpoint()); + } + final OAuthRequest request = prepareRequestTokenRequest(); + + 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); + } + } - config.log("response status code: " + response.getCode()); - config.log("response body: " + body); - return api.getRequestTokenExtractor().extract(body); + 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()); - final OAuthRequestAsync request - = new OAuthRequestAsync(api.getRequestTokenVerb(), api.getRequestTokenEndpoint(), this); - prepareRequestTokenRequest(request); - return request.sendAsync(callback, new OAuthRequestAsync.ResponseConverter() { + 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.getBody()); + final OAuth1RequestToken token = getApi().getRequestTokenExtractor().extract(response); + response.close(); + return token; } }); } - protected void prepareRequestTokenRequest(AbstractRequest request) { - final OAuthConfig config = getConfig(); - config.log("setting oauth_callback to " + config.getCallback()); - request.addOAuthParameter(OAuthConstants.CALLBACK, config.getCallback()); + protected OAuthRequest prepareRequestTokenRequest() { + final OAuthRequest request = new OAuthRequest(api.getRequestTokenVerb(), api.getRequestTokenEndpoint()); + 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; } - private void addOAuthParams(AbstractRequest request, String tokenSecret) { - final OAuthConfig config = getConfig(); + protected void addOAuthParams(OAuthRequest request, String tokenSecret) { 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 OAuth1AccessToken getAccessToken(OAuth1RequestToken requestToken, String oauthVerifier) + throws IOException, InterruptedException, ExecutionException { + if (isDebug()) { + log("obtaining access token from %s", api.getAccessTokenEndpoint()); + } + final OAuthRequest request = prepareAccessTokenRequest(requestToken, oauthVerifier); + try ( Response response = execute(request)) { + return api.getAccessTokenExtractor().extract(response); + } } - public final OAuth1AccessToken getAccessToken(OAuth1RequestToken requestToken, String oauthVerifier) - throws IOException { - final OAuthConfig config = getConfig(); - config.log("obtaining access token from " + api.getAccessTokenEndpoint()); - final OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint(), this); - prepareAccessTokenRequest(request, requestToken, oauthVerifier); - final Response response = request.send(); - return api.getAccessTokenExtractor().extract(response.getBody()); + 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()); - final OAuthRequestAsync request - = new OAuthRequestAsync(api.getAccessTokenVerb(), api.getAccessTokenEndpoint(), this); - prepareAccessTokenRequest(request, requestToken, oauthVerifier); - return request.sendAsync(callback, new OAuthRequestAsync.ResponseConverter() { + 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.getBody()); + final OAuth1AccessToken token = getApi().getAccessTokenExtractor().extract(response); + response.close(); + return token; } }); } - protected void prepareAccessTokenRequest(AbstractRequest request, OAuth1RequestToken requestToken, - String oauthVerifier) { - final OAuthConfig config = getConfig(); + protected OAuthRequest prepareAccessTokenRequest(OAuth1RequestToken requestToken, String oauthVerifier) { + final OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint()); 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; } - public void signRequest(OAuth1AccessToken token, AbstractRequest request) { - final OAuthConfig config = getConfig(); - config.log("signing request: " + request.getCompleteUrl()); + public void signRequest(OAuth1AccessToken token, OAuthRequest request) { + 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); } @@ -152,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 @@ -162,36 +181,36 @@ public String getAuthorizationUrl(OAuth1RequestToken requestToken) { return api.getAuthorizationUrl(requestToken); } - private String getSignature(AbstractRequest request, String tokenSecret) { - final OAuthConfig config = getConfig(); - config.log("generating signature..."); - config.log("using base64 encoder: " + Base64Encoder.type()); + private String getSignature(OAuthRequest request, String tokenSecret) { + 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; } - private void appendSignature(AbstractRequest request) { - final OAuthConfig config = getConfig(); - switch (config.getSignatureType()) { - case Header: - config.log("using Http Header signature"); + protected void appendSignature(OAuthRequest request) { + final OAuth1SignatureType signatureType = api.getSignatureType(); + switch (signatureType) { + 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: - throw new IllegalStateException("Unknown new Signature Type '" + config.getSignatureType() + "'."); + throw new IllegalStateException("Unknown new Signature Type '" + signatureType + "'."); } } 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 43bf4c7fa..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,217 +1,676 @@ 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.model.AbstractRequest; +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.OAuthRequestAsync; 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 { 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); } - //sync version, protected to facilitate mocking - protected OAuth2AccessToken sendAccessTokenRequestSync(OAuthRequest request) throws IOException { - return api.getAccessTokenExtractor().extract(request.send().getBody()); + public DefaultApi20 getApi() { + return api; } - //async version, protected to facilitate mocking - protected Future sendAccessTokenRequestAsync(OAuthRequestAsync request, + 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 { + 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 + protected Future sendAccessTokenRequestAsync(OAuthRequest request) { + return sendAccessTokenRequestAsync(request, null); + } + + //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 request.sendAsync(callback, new OAuthRequestAsync.ResponseConverter() { + return execute(request, callback, new OAuthRequest.ResponseConverter() { @Override public OAuth2AccessToken convert(Response response) throws IOException { - return getApi().getAccessTokenExtractor().extract(response.getBody()); + 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 OAuth2AccessToken getAccessToken(String code) throws IOException { - final OAuthRequest request = createAccessTokenRequest(code, - new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint(), this)); + // ===== get AccessToken authorisation code flow methods ===== + protected OAuthRequest createAccessTokenRequest(AccessTokenRequestParams params) { + final OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint()); - return sendAccessTokenRequestSync(request); + 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 Future getAccessTokenAsync(AccessTokenRequestParams params) { + return getAccessToken(params, null); + } + + 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 getAccessTokenAsync(String code, + public Future getAccessToken(AccessTokenRequestParams params, OAuthAsyncRequestCallback callback) { - final OAuthRequestAsync request = createAccessTokenRequest(code, - new OAuthRequestAsync(api.getAccessTokenVerb(), api.getAccessTokenEndpoint(), this)); + return sendAccessTokenRequestAsync(createAccessTokenRequest(params), callback); + } - return sendAccessTokenRequestAsync(request, callback); + public Future getAccessToken(String code, + OAuthAsyncRequestCallback callback) { + return getAccessToken(AccessTokenRequestParams.create(code), callback); } - protected T createAccessTokenRequest(String code, T request) { - final OAuthConfig config = getConfig(); - request.addParameter(OAuthConstants.CLIENT_ID, config.getApiKey()); - request.addParameter(OAuthConstants.CLIENT_SECRET, config.getApiSecret()); - 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 OAuth2AccessToken refreshAccessToken(String refreshToken) throws IOException { - final OAuthRequest request = createRefreshTokenRequest(refreshToken, - new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint(), this)); + public Future refreshAccessTokenAsync(String refreshToken) { + return refreshAccessToken(refreshToken, (OAuthAsyncRequestCallback) null); + } + + public Future refreshAccessTokenAsync(String refreshToken, String scope) { + return refreshAccessToken(refreshToken, scope, null); + } + + public OAuth2AccessToken refreshAccessToken(String refreshToken) + throws IOException, InterruptedException, ExecutionException { + 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 refreshAccessTokenAsync(String refreshToken, + public Future refreshAccessToken(String refreshToken, OAuthAsyncRequestCallback callback) { - final OAuthRequestAsync request = createRefreshTokenRequest(refreshToken, - new OAuthRequestAsync(api.getAccessTokenVerb(), api.getRefreshTokenEndpoint(), this)); + final OAuthRequest request = createRefreshTokenRequest(refreshToken, null); return sendAccessTokenRequestAsync(request, callback); } - protected T createRefreshTokenRequest(String refreshToken, T request) { - if (refreshToken == null || refreshToken.isEmpty()) { - throw new IllegalArgumentException("The refreshToken cannot be null or empty"); + 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); } - final OAuthConfig config = getConfig(); - request.addParameter(OAuthConstants.CLIENT_ID, config.getApiKey()); - request.addParameter(OAuthConstants.CLIENT_SECRET, config.getApiSecret()); - 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) throws IOException { - final OAuthRequest request = createAccessTokenPasswordGrantRequest(uname, password, - new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint(), this)); + 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(username, password, scope); return sendAccessTokenRequestSync(request); } + 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 OAuthRequestAsync request = createAccessTokenPasswordGrantRequest(uname, password, - new OAuthRequestAsync(api.getAccessTokenVerb(), api.getAccessTokenEndpoint(), this)); + final OAuthRequest request = createAccessTokenPasswordGrantRequest(username, password, null); return sendAccessTokenRequestAsync(request, callback); } - protected T createAccessTokenPasswordGrantRequest(String username, String password, - T request) { - final OAuthConfig config = getConfig(); - request.addParameter(OAuthConstants.USERNAME, username); - request.addParameter(OAuthConstants.PASSWORD, 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()); + + api.getClientAuthentication().addClientAuthentication(request, getApiKey(), getApiSecret()); - final String scope = config.getScope(); 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); + } + + 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 void signRequest(OAuth2AccessToken accessToken, AbstractRequest request) { - request.addQuerystringParameter(OAuthConstants.ACCESS_TOKEN, accessToken.getAccessToken()); + 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(); - for (String param : redirectLocation.substring(redirectLocation.indexOf('?') + 1).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 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); + } + } + + 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 47b40b497..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 @@ -1,73 +1,71 @@ package com.github.scribejava.core.oauth; -import com.github.scribejava.core.exceptions.OAuthException; -import com.github.scribejava.core.model.AbstractRequest; -import static com.github.scribejava.core.model.AbstractRequest.DEFAULT_CONTENT_TYPE; -import com.github.scribejava.core.model.ForceTypeOfHttpRequest; +import com.github.scribejava.core.httpclient.HttpClientProvider; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; +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.OAuthConstants; -import com.github.scribejava.core.model.OAuthRequestAsync; -import com.github.scribejava.core.model.ScribeJavaConfig; -import com.github.scribejava.core.model.Verb; +import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.model.Response; +import java.io.Closeable; +import java.io.File; + import java.io.IOException; -import java.util.Map; +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. - */ -public abstract class OAuthService { - - private final OAuthConfig config; - private final com.ning.http.client.AsyncHttpClient ningAsyncHttpClient; - private final org.asynchttpclient.AsyncHttpClient ahcAsyncHttpClient; - - public OAuthService(OAuthConfig config) { - this.config = config; - final ForceTypeOfHttpRequest forceTypeOfHttpRequest = ScribeJavaConfig.getForceTypeOfHttpRequests(); - final com.ning.http.client.AsyncHttpClientConfig ningConfig = config.getNingAsyncHttpClientConfig(); - final org.asynchttpclient.AsyncHttpClientConfig ahcConfig = config.getAhcAsyncHttpClientConfig(); - - if (ningConfig == null && ahcConfig == null) { - if (ForceTypeOfHttpRequest.FORCE_ASYNC_ONLY_HTTP_REQUESTS == forceTypeOfHttpRequest) { - throw new OAuthException("Cannot use sync operations, only async"); - } - if (ForceTypeOfHttpRequest.PREFER_ASYNC_ONLY_HTTP_REQUESTS == forceTypeOfHttpRequest) { - config.log("Cannot use sync operations, only async"); - } - ningAsyncHttpClient = null; - ahcAsyncHttpClient = null; +public abstract class OAuthService implements Closeable { + + private final String apiKey; + private final String apiSecret; + private final String callback; + private final String userAgent; + private final HttpClient httpClient; + 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 { - if (ForceTypeOfHttpRequest.FORCE_SYNC_ONLY_HTTP_REQUESTS == forceTypeOfHttpRequest) { - throw new OAuthException("Cannot use async operations, only sync"); - } - if (ForceTypeOfHttpRequest.PREFER_SYNC_ONLY_HTTP_REQUESTS == forceTypeOfHttpRequest) { - config.log("Cannot use async operations, only sync"); - } + this.httpClient = httpClient == null ? getClient(httpClientConfig) : httpClient; + } + } - if (ahcConfig == null) { - ningAsyncHttpClient = NingProvider.createClient(config.getNingAsyncHttpProviderClassName(), ningConfig); - ahcAsyncHttpClient = null; - } else { - ahcAsyncHttpClient = AHCProvider.createClient(ahcConfig); - ningAsyncHttpClient = null; + private static HttpClient getClient(HttpClientConfig config) { + for (HttpClientProvider provider : ServiceLoader.load(HttpClientProvider.class)) { + final HttpClient client = provider.createClient(config); + if (client != null) { + return client; } } + return null; } - public void closeAsyncClient() throws IOException { - if (ahcAsyncHttpClient == null) { - ningAsyncHttpClient.close(); - } else { - ahcAsyncHttpClient.close(); - } + @Override + public void close() throws IOException { + httpClient.close(); + } + + public String getApiKey() { + return apiKey; + } + + public String getApiSecret() { + return apiSecret; } - public OAuthConfig getConfig() { - return config; + public String getCallback() { + return callback; } /** @@ -77,97 +75,74 @@ public OAuthConfig getConfig() { */ public abstract String getVersion(); - public Future executeAsync(Map headers, Verb httpVerb, String completeUrl, - String bodyContents, OAuthAsyncRequestCallback callback, - OAuthRequestAsync.ResponseConverter converter) { - if (ahcAsyncHttpClient == null) { - return NingProvider.ningExecuteAsync(ningAsyncHttpClient, config.getUserAgent(), headers, httpVerb, - completeUrl, bodyContents, callback, converter); - } else { - return AHCProvider.ahcExecuteAsync(ahcAsyncHttpClient, config.getUserAgent(), headers, httpVerb, - completeUrl, bodyContents, callback, converter); - } + public Future executeAsync(OAuthRequest request) { + return execute(request, null); } - private static class NingProvider { - - private static com.ning.http.client.AsyncHttpClient createClient(String ningAsyncHttpProviderClassName, - com.ning.http.client.AsyncHttpClientConfig ningConfig) { - return ningAsyncHttpProviderClassName == null - ? new com.ning.http.client.AsyncHttpClient(ningConfig) - : new com.ning.http.client.AsyncHttpClient(ningAsyncHttpProviderClassName, ningConfig); - } - - private static Future ningExecuteAsync(com.ning.http.client.AsyncHttpClient ningAsyncHttpClient, - String userAgent, Map headers, Verb httpVerb, String completeUrl, String bodyContents, - OAuthAsyncRequestCallback callback, OAuthRequestAsync.ResponseConverter converter) { - final com.ning.http.client.AsyncHttpClient.BoundRequestBuilder boundRequestBuilder; - switch (httpVerb) { - case GET: - boundRequestBuilder = ningAsyncHttpClient.prepareGet(completeUrl); - break; - case POST: - com.ning.http.client.AsyncHttpClient.BoundRequestBuilder requestBuilder - = ningAsyncHttpClient.preparePost(completeUrl); - if (!headers.containsKey(AbstractRequest.CONTENT_TYPE)) { - requestBuilder = requestBuilder.addHeader(AbstractRequest.CONTENT_TYPE, DEFAULT_CONTENT_TYPE); - } - boundRequestBuilder = requestBuilder.setBody(bodyContents); - break; - default: - throw new IllegalArgumentException("message build error: unknown verb type"); - } + public Future execute(OAuthRequest request, OAuthAsyncRequestCallback callback) { + return execute(request, callback, null); + } - for (Map.Entry header : headers.entrySet()) { - boundRequestBuilder.addHeader(header.getKey(), header.getValue()); - } - if (userAgent != null) { - boundRequestBuilder.setHeader(OAuthConstants.USER_AGENT_HEADER_NAME, userAgent); - } + public Future execute(OAuthRequest request, OAuthAsyncRequestCallback callback, + OAuthRequest.ResponseConverter converter) { - return boundRequestBuilder - .execute(new com.github.scribejava.core.async.ning.OAuthAsyncCompletionHandler<>( - callback, converter)); + final File filePayload = request.getFilePayload(); + if (filePayload != null) { + return httpClient.executeAsync(userAgent, request.getHeaders(), request.getVerb(), request.getCompleteUrl(), + filePayload, callback, converter); + } else if (request.getStringPayload() != null) { + return httpClient.executeAsync(userAgent, request.getHeaders(), request.getVerb(), request.getCompleteUrl(), + request.getStringPayload(), callback, converter); + } else { + return httpClient.executeAsync(userAgent, request.getHeaders(), request.getVerb(), request.getCompleteUrl(), + request.getByteArrayPayload(), callback, converter); } } - private static class AHCProvider { - - private static org.asynchttpclient.AsyncHttpClient createClient( - org.asynchttpclient.AsyncHttpClientConfig ahcConfig) { - return new org.asynchttpclient.DefaultAsyncHttpClient(ahcConfig); + public Response execute(OAuthRequest request) throws InterruptedException, ExecutionException, IOException { + final File filePayload = request.getFilePayload(); + if (filePayload != null) { + return httpClient.execute(userAgent, request.getHeaders(), request.getVerb(), request.getCompleteUrl(), + filePayload); + } else if (request.getStringPayload() != null) { + 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(userAgent, request.getHeaders(), request.getVerb(), request.getCompleteUrl(), + request.getByteArrayPayload()); } + } - private static Future ahcExecuteAsync(org.asynchttpclient.AsyncHttpClient ahcAsyncHttpClient, - String userAgent, Map headers, Verb httpVerb, String completeUrl, String bodyContents, - OAuthAsyncRequestCallback callback, OAuthRequestAsync.ResponseConverter converter) { - final org.asynchttpclient.BoundRequestBuilder boundRequestBuilder; - switch (httpVerb) { - case GET: - boundRequestBuilder = ahcAsyncHttpClient.prepareGet(completeUrl); - break; - case POST: - org.asynchttpclient.BoundRequestBuilder requestBuilder - = ahcAsyncHttpClient.preparePost(completeUrl); - if (!headers.containsKey(AbstractRequest.CONTENT_TYPE)) { - requestBuilder = requestBuilder.addHeader(AbstractRequest.CONTENT_TYPE, DEFAULT_CONTENT_TYPE); - } - boundRequestBuilder = requestBuilder.setBody(bodyContents); - break; - default: - throw new IllegalArgumentException("message build error: unknown verb type"); - } - - for (Map.Entry header : headers.entrySet()) { - boundRequestBuilder.addHeader(header.getKey(), header.getValue()); - } - if (userAgent != null) { - boundRequestBuilder.setHeader(OAuthConstants.USER_AGENT_HEADER_NAME, userAgent); - } + /** + * 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); + } + } - return boundRequestBuilder - .execute(new com.github.scribejava.core.async.ahc.OAuthAsyncCompletionHandler<>( - callback, converter)); + /** + * 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/main/java/com/github/scribejava/core/utils/Preconditions.java b/scribejava-core/src/main/java/com/github/scribejava/core/utils/Preconditions.java index 0858fc89d..19e1df083 100644 --- a/scribejava-core/src/main/java/com/github/scribejava/core/utils/Preconditions.java +++ b/scribejava-core/src/main/java/com/github/scribejava/core/utils/Preconditions.java @@ -1,9 +1,5 @@ package com.github.scribejava.core.utils; -import java.util.Locale; -import java.util.regex.Pattern; -import com.github.scribejava.core.model.OAuthConstants; - /** * Utils for checking preconditions and invariants */ @@ -11,9 +7,6 @@ public abstract class Preconditions { private static final String DEFAULT_MESSAGE = "Received an invalid parameter"; - // scheme = alpha *( alpha | digit | "+" | "-" | "." ) - private static final String URL_REGEXP = "^[a-zA-Z][a-zA-Z0-9+.-]*://\\S+"; - /** * Checks that an object is not null. * @@ -35,40 +28,25 @@ public static void checkNotNull(Object object, String errorMsg) { * @throws IllegalArgumentException if the string is null or empty */ public static void checkEmptyString(String string, String errorMsg) { - check(string != null && !string.trim().isEmpty(), errorMsg); - } - - /** - * Checks that a URL is valid - * - * @param url any string - * @param errorMsg error message - */ - public static void checkValidUrl(String url, String errorMsg) { - checkEmptyString(url, errorMsg); - check(isUrl(url), errorMsg); + check(hasText(string), errorMsg); } - /** - * Checks that a URL is a valid OAuth callback - * - * @param url any string - * @param errorMsg error message - */ - public static void checkValidOAuthCallback(String url, String errorMsg) { - checkEmptyString(url, errorMsg); - if (url.toLowerCase(Locale.getDefault()).compareToIgnoreCase(OAuthConstants.OUT_OF_BAND) != 0) { - check(isUrl(url), errorMsg); + public static boolean hasText(String str) { + if (str == null || str.isEmpty()) { + return false; } - } - - private static boolean isUrl(String url) { - return Pattern.compile(URL_REGEXP).matcher(url).matches(); + final int strLen = str.length(); + for (int i = 0; i < strLen; i++) { + if (!Character.isWhitespace(str.charAt(i))) { + return true; + } + } + return false; } private static void check(boolean requirements, String error) { if (!requirements) { - throw new IllegalArgumentException(error == null || error.trim().length() <= 0 ? DEFAULT_MESSAGE : error); + throw new IllegalArgumentException(hasText(error) ? error : DEFAULT_MESSAGE); } } diff --git a/scribejava-core/src/main/resources/META-INF/services/com.github.scribejava.core.httpclient.HttpClientProvider b/scribejava-core/src/main/resources/META-INF/services/com.github.scribejava.core.httpclient.HttpClientProvider new file mode 100644 index 000000000..7df053c24 --- /dev/null +++ b/scribejava-core/src/main/resources/META-INF/services/com.github.scribejava.core.httpclient.HttpClientProvider @@ -0,0 +1,2 @@ +com.github.scribejava.core.httpclient.jdk.JDKHttpProvider + 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/ObjectMother.java b/scribejava-core/src/test/java/com/github/scribejava/core/ObjectMother.java index 30482b2c6..07e4081ec 100644 --- a/scribejava-core/src/test/java/com/github/scribejava/core/ObjectMother.java +++ b/scribejava-core/src/test/java/com/github/scribejava/core/ObjectMother.java @@ -1,16 +1,13 @@ package com.github.scribejava.core; -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.Verb; -import com.github.scribejava.core.oauth.OAuth20Service; public abstract class ObjectMother { public static OAuthRequest createSampleOAuthRequest() { - final OAuthRequest request = new OAuthRequest(Verb.GET, "http://example.com", - new OAuth20Service(null, new OAuthConfig("test", "test"))); + final OAuthRequest request = new OAuthRequest(Verb.GET, "http://example.com"); request.addOAuthParameter(OAuthConstants.TIMESTAMP, "123456"); request.addOAuthParameter(OAuthConstants.CONSUMER_KEY, "AS#$^*@&"); request.addOAuthParameter(OAuthConstants.CALLBACK, "http://example/callback"); @@ -19,8 +16,7 @@ public static OAuthRequest createSampleOAuthRequest() { } public static OAuthRequest createSampleOAuthRequestPort80() { - final OAuthRequest request = new OAuthRequest(Verb.GET, "http://example.com:80", - new OAuth20Service(null, new OAuthConfig("test", "test"))); + final OAuthRequest request = new OAuthRequest(Verb.GET, "http://example.com:80"); request.addOAuthParameter(OAuthConstants.TIMESTAMP, "123456"); request.addOAuthParameter(OAuthConstants.CONSUMER_KEY, "AS#$^*@&"); request.addOAuthParameter(OAuthConstants.CALLBACK, "http://example/callback"); @@ -29,8 +25,7 @@ public static OAuthRequest createSampleOAuthRequestPort80() { } public static OAuthRequest createSampleOAuthRequestPort80v2() { - final OAuthRequest request = new OAuthRequest(Verb.GET, "http://example.com:80/test", - new OAuth20Service(null, new OAuthConfig("test", "test"))); + final OAuthRequest request = new OAuthRequest(Verb.GET, "http://example.com:80/test"); request.addOAuthParameter(OAuthConstants.TIMESTAMP, "123456"); request.addOAuthParameter(OAuthConstants.CONSUMER_KEY, "AS#$^*@&"); request.addOAuthParameter(OAuthConstants.CALLBACK, "http://example/callback"); @@ -39,8 +34,7 @@ public static OAuthRequest createSampleOAuthRequestPort80v2() { } public static OAuthRequest createSampleOAuthRequestPort8080() { - final OAuthRequest request = new OAuthRequest(Verb.GET, "http://example.com:8080", - new OAuth20Service(null, new OAuthConfig("test", "test"))); + final OAuthRequest request = new OAuthRequest(Verb.GET, "http://example.com:8080"); request.addOAuthParameter(OAuthConstants.TIMESTAMP, "123456"); request.addOAuthParameter(OAuthConstants.CONSUMER_KEY, "AS#$^*@&"); request.addOAuthParameter(OAuthConstants.CALLBACK, "http://example/callback"); @@ -49,8 +43,7 @@ public static OAuthRequest createSampleOAuthRequestPort8080() { } public static OAuthRequest createSampleOAuthRequestPort443() { - final OAuthRequest request = new OAuthRequest(Verb.GET, "https://example.com:443", - new OAuth20Service(null, new OAuthConfig("test", "test"))); + final OAuthRequest request = new OAuthRequest(Verb.GET, "https://example.com:443"); request.addOAuthParameter(OAuthConstants.TIMESTAMP, "123456"); request.addOAuthParameter(OAuthConstants.CONSUMER_KEY, "AS#$^*@&"); request.addOAuthParameter(OAuthConstants.CALLBACK, "http://example/callback"); @@ -59,8 +52,7 @@ public static OAuthRequest createSampleOAuthRequestPort443() { } public static OAuthRequest createSampleOAuthRequestPort443v2() { - final OAuthRequest request = new OAuthRequest(Verb.GET, "https://example.com:443/test", - new OAuth20Service(null, new OAuthConfig("test", "test"))); + final OAuthRequest request = new OAuthRequest(Verb.GET, "https://example.com:443/test"); request.addOAuthParameter(OAuthConstants.TIMESTAMP, "123456"); request.addOAuthParameter(OAuthConstants.CONSUMER_KEY, "AS#$^*@&"); request.addOAuthParameter(OAuthConstants.CALLBACK, "http://example/callback"); 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 57f7d62e2..000000000 --- a/scribejava-core/src/test/java/com/github/scribejava/core/builder/ServiceBuilderTest.java +++ /dev/null @@ -1,97 +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.model.SignatureType; -import com.github.scribejava.core.oauth.OAuth20Service; - -public class ServiceBuilderTest { - - private ServiceBuilder builder; - private ApiMock api; - - @Before - public void setUp() { - builder = new ServiceBuilder(); - 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); - assertEquals(config.getSignatureType(), SignatureType.Header); - } - - @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 - public void shouldAcceptASignatureType() { - builder.apiKey("key").apiSecret("secret").signatureType(SignatureType.QueryString).build(api); - - final OAuthConfig config = api.getConfig(); - assertEquals(config.getApiKey(), "key"); - assertEquals(config.getApiSecret(), "secret"); - assertEquals(config.getSignatureType(), SignatureType.QueryString); - } - - @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 3eb4b7404..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 @@ -5,10 +5,10 @@ import org.junit.Test; import com.github.scribejava.core.ObjectMother; import com.github.scribejava.core.exceptions.OAuthParametersMissingException; -import com.github.scribejava.core.model.OAuthConfig; import com.github.scribejava.core.model.OAuthRequest; import com.github.scribejava.core.model.Verb; -import com.github.scribejava.core.oauth.OAuth20Service; +import static org.junit.Assert.assertThrows; +import org.junit.function.ThrowingRunnable; public class BaseStringExtractorTest { @@ -85,16 +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", - new OAuth20Service(null, new OAuthConfig("test", "test"))); - extractor.extract(request); + final OAuthRequest request = new OAuthRequest(Verb.GET, "http://example.com"); + 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 356a1fbba..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,14 +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.OAuthConfig; import com.github.scribejava.core.model.OAuthRequest; import com.github.scribejava.core.model.Verb; -import com.github.scribejava.core.oauth.OAuth20Service; import com.github.scribejava.core.ObjectMother; +import org.junit.function.ThrowingRunnable; public class HeaderExtractorTest { @@ -38,22 +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", - new OAuth20Service(null, new OAuthConfig("test", "test"))); - extractor.extract(emptyRequest); + final OAuthRequest emptyRequest = new OAuthRequest(Verb.GET, "http://example.com"); + 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 7cf3feb95..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 @@ -1,10 +1,17 @@ package com.github.scribejava.core.extractors; +import com.github.scribejava.core.model.Response; import org.junit.Before; import org.junit.Test; import com.github.scribejava.core.exceptions.OAuthException; import com.github.scribejava.core.model.OAuth1Token; + +import java.io.IOException; +import java.util.Collections; + import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import org.junit.function.ThrowingRunnable; public class OAuth1AccessTokenExtractorTest { @@ -16,58 +23,98 @@ public void setUp() { } @Test - public void shouldExtractTokenFromOAuthStandardResponse() { - final String response = "oauth_token=hh5s93j4hdidpola&oauth_token_secret=hdhd0244k9j7ao03"; - final OAuth1Token extracted = extractor.extract(response); + public void shouldExtractTokenFromOAuthStandardResponse() throws IOException { + 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() { - final String response = "oauth_token_secret=hh5s93j4hdidpola&oauth_token=hdhd0244k9j7ao03"; - final OAuth1Token extracted = extractor.extract(response); + public void shouldExtractTokenFromInvertedOAuthStandardResponse() throws IOException { + 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() { - final String response = "oauth_token=hh5s93j4hdidpola&oauth_token_secret=hdhd0244k9j7ao03" + public void shouldExtractTokenFromResponseWithCallbackConfirmed() throws IOException { + final String responseBody = "oauth_token=hh5s93j4hdidpola&oauth_token_secret=hdhd0244k9j7ao03" + "&callback_confirmed=true"; - final OAuth1Token extracted = extractor.extract(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() { - final String response = "oauth_token=hh5s93j4hdidpola&oauth_token_secret="; - final OAuth1Token extracted = extractor.extract(response); + public void shouldExtractTokenWithEmptySecret() throws IOException { + 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() { - final String response = "oauth_secret=hh5s93j4hdidpola&callback_confirmed=true"; - extractor.extract(response); + public void shouldThrowExceptionIfTokenIsAbsent() throws IOException { + 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); + } + }); + } + } + + public void shouldThrowExceptionIfSecretIsAbsent() throws IOException { + 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 = OAuthException.class) - public void shouldThrowExceptionIfSecretIsAbsent() { - final String response = "oauth_token=hh5s93j4hdidpola&callback_confirmed=true"; - extractor.extract(response); + public void shouldThrowExceptionIfResponseIsNull() throws IOException { + 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 shouldThrowExceptionIfResponseIsNull() { - extractor.extract(null); + public void shouldThrowExceptionIfResponseIsEmptyString() throws IOException { + final String responseBody = ""; + try (Response response = ok(responseBody)) { + assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(response); + } + }); + } } - @Test(expected = IllegalArgumentException.class) - public void shouldThrowExceptionIfResponseIsEmptyString() { - final String response = ""; - extractor.extract(response); + 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/extractors/OAuth2AccessTokenExtractorTest.java b/scribejava-core/src/test/java/com/github/scribejava/core/extractors/OAuth2AccessTokenExtractorTest.java index fa66956d5..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 @@ -1,10 +1,17 @@ package com.github.scribejava.core.extractors; +import com.github.scribejava.core.model.Response; import org.junit.Before; import org.junit.Test; import com.github.scribejava.core.exceptions.OAuthException; import com.github.scribejava.core.model.OAuth2AccessToken; + +import java.io.IOException; +import java.util.Collections; + import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import org.junit.function.ThrowingRunnable; public class OAuth2AccessTokenExtractorTest { @@ -16,29 +23,38 @@ public void setUp() { } @Test - public void shouldExtractTokenFromOAuthStandardResponse() { - final String response = "access_token=166942940015970|2.2ltzWXYNDjCtg5ZDVVJJeg__.3600.1295816400-548517159" + public void shouldExtractTokenFromOAuthStandardResponse() throws IOException { + final String responseBody = "access_token=166942940015970|2.2ltzWXYNDjCtg5ZDVVJJeg__.3600.1295816400-548517159" + "|RsXNdKrpxg8L6QNLWcs2TVTmcaE"; - final OAuth2AccessToken extracted = extractor.extract(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() { - final String response = "access_token=166942940015970|2.2ltzWXYNDjCtg5ZDVVJJeg__.3600.1295816400-548517159" + public void shouldExtractTokenFromResponseWithExpiresParam() throws IOException { + final String responseBody = "access_token=166942940015970|2.2ltzWXYNDjCtg5ZDVVJJeg__.3600.1295816400-548517159" + "|RsXNdKrpxg8L6QNLWcs2TVTmcaE&expires_in=5108"; - final OAuth2AccessToken extracted = extractor.extract(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()); } @Test - public void shouldExtractTokenFromResponseWithExpiresAndRefreshParam() { - final String response = "access_token=166942940015970|2.2ltzWXYNDjCtg5ZDVVJJeg__.3600.1295816400-548517159" + public void shouldExtractTokenFromResponseWithExpiresAndRefreshParam() throws IOException { + 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(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()); @@ -47,26 +63,67 @@ public void shouldExtractTokenFromResponseWithExpiresAndRefreshParam() { } @Test - public void shouldExtractTokenFromResponseWithManyParameters() { - final String response = "access_token=foo1234&other_stuff=yeah_we_have_this_too&number=42"; - final OAuth2AccessToken extracted = extractor.extract(response); + public void shouldExtractTokenFromResponseWithManyParameters() throws IOException { + 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 shouldThrowExceptionIfTokenIsAbsent() { - final String response = "&expires=5108"; - extractor.extract(response); + 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 responseBody = "&expires=5108"; + try (Response response = ok(responseBody)) { + assertThrows(OAuthException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(response); + } + }); + } + } + + public void shouldThrowExceptionIfResponseIsNull() throws IOException { + try (Response response = ok(null)) { + assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(response); + } + }); + } + } + + public void shouldThrowExceptionIfResponseIsEmptyString() throws IOException { + final String responseBody = ""; + try (Response response = ok(responseBody)) { + assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(response); + } + }); + } } - @Test(expected = IllegalArgumentException.class) - public void shouldThrowExceptionIfResponseIsNull() { - extractor.extract(null); + private static Response ok(String body) { + return new Response(200, /* message */ null, /* headers */ Collections.emptyMap(), body); } - @Test(expected = IllegalArgumentException.class) - public void shouldThrowExceptionIfResponseIsEmptyString() { - final String response = ""; - extractor.extract(response); + 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 9b2098e6c..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 @@ -1,27 +1,126 @@ package com.github.scribejava.core.extractors; 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.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() { - final OAuth2AccessToken token = extractor.extract(RESPONSE); - assertEquals(token.getAccessToken(), "I0122HHJKLEM21F3WLPYHDKGKZULAUO4SGMV3ABKFTDT3T3X"); + public void shouldParseResponse() throws IOException { + 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()); + } + + public void shouldThrowExceptionIfForNullParameters() throws IOException { + try (Response response = ok(null)) { + assertThrows(IllegalArgumentException.class, new ThrowingRunnable() { + @Override + public void run() throws Throwable { + extractor.extract(response); + } + }); + } + } + + public void shouldThrowExceptionIfForEmptyStrings() throws IOException { + 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 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()); } - @Test(expected = IllegalArgumentException.class) - public void shouldThrowExceptionIfForNullParameters() { - extractor.extract(null); + private static Response ok(String body) { + return new Response(200, /* message */ null, /* headers */ Collections.emptyMap(), body); } - @Test(expected = IllegalArgumentException.class) - public void shouldThrowExceptionIfForEmptyStrings() { - extractor.extract(""); + 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/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/ConnectionStub.java b/scribejava-core/src/test/java/com/github/scribejava/core/model/ConnectionStub.java deleted file mode 100644 index b7465527d..000000000 --- a/scribejava-core/src/test/java/com/github/scribejava/core/model/ConnectionStub.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.github.scribejava.core.model; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class ConnectionStub extends HttpURLConnection { - - private final Map headers = new HashMap<>(); - private final Map> responseHeaders = new HashMap<>(); - private int inputStreamCalled; - - public ConnectionStub() throws MalformedURLException { - super(new URL("http://example.com")); - } - - @Override - public void setRequestProperty(String key, String value) { - headers.put(key, value); - } - - @Override - public String getRequestProperty(String s) { - return headers.get(s); - } - - public Map getHeaders() { - return headers; - } - - @Override - public int getResponseCode() throws IOException { - return 200; - } - - @Override - public InputStream getInputStream() throws IOException { - inputStreamCalled++; - return new ByteArrayInputStream("contents".getBytes()); - } - - public int getTimesCalledInpuStream() { - return inputStreamCalled; - } - - @Override - public OutputStream getOutputStream() throws IOException { - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - baos.write("contents".getBytes()); - return baos; - } - - @Override - public Map> getHeaderFields() { - return responseHeaders; - } - - public void addResponseHeader(String key, String value) { - responseHeaders.put(key, Arrays.asList(value)); - } - - @Override - public void connect() { - } - - @Override - public void disconnect() { - } - - @Override - public boolean usingProxy() { - return false; - } - -} diff --git a/scribejava-core/src/test/java/com/github/scribejava/core/model/ForceTypeOfHttpRequestTest.java b/scribejava-core/src/test/java/com/github/scribejava/core/model/ForceTypeOfHttpRequestTest.java deleted file mode 100644 index 9e60f3fb2..000000000 --- a/scribejava-core/src/test/java/com/github/scribejava/core/model/ForceTypeOfHttpRequestTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.github.scribejava.core.model; - -import java.util.concurrent.ExecutionException; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import com.github.scribejava.core.exceptions.OAuthException; -import com.github.scribejava.core.oauth.OAuth20Service; -import com.github.scribejava.core.oauth.OAuthService; - -public class ForceTypeOfHttpRequestTest { - - @Rule - public ExpectedException expectedException = ExpectedException.none(); - - private OAuthRequest request; - private OAuthRequestAsync requestAsync; - - @Before - public void setUp() { - ScribeJavaConfig.setForceTypeOfHttpRequests(ForceTypeOfHttpRequest.NONE); - final OAuthService oAuthService = new OAuth20Service(null, new OAuthConfig("test", "test")); - request = new OAuthRequest(Verb.GET, "http://example.com?qsparam=value&other+param=value+with+spaces", - oAuthService); - requestAsync = new OAuthRequestAsync(Verb.GET, "http://example.com?qsparam=value&other+param=value+with+spaces", - oAuthService); - } - - @Test - public void shouldNotSendSyncWithForceParameter() { - expectedException.expect(OAuthException.class); - expectedException.expectMessage("Cannot use sync operations, only async"); - ScribeJavaConfig.setForceTypeOfHttpRequests(ForceTypeOfHttpRequest.FORCE_ASYNC_ONLY_HTTP_REQUESTS); - request.send(); - } - - @Test - public void shouldNotSendAsyncWithForceParameter() throws ExecutionException, InterruptedException { - expectedException.expect(OAuthException.class); - expectedException.expectMessage("Cannot use async operations, only sync"); - ScribeJavaConfig.setForceTypeOfHttpRequests(ForceTypeOfHttpRequest.FORCE_SYNC_ONLY_HTTP_REQUESTS); - requestAsync.sendAsync(null).get(); - } -} 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 d1bae5bfa..32d5d6731 100644 --- a/scribejava-core/src/test/java/com/github/scribejava/core/model/OAuthRequestTest.java +++ b/scribejava-core/src/test/java/com/github/scribejava/core/model/OAuthRequestTest.java @@ -1,9 +1,11 @@ package com.github.scribejava.core.model; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; import org.junit.Before; import org.junit.Test; -import com.github.scribejava.core.oauth.OAuth20Service; +import org.junit.function.ThrowingRunnable; public class OAuthRequestTest { @@ -11,8 +13,7 @@ public class OAuthRequestTest { @Before public void setUp() { - request = new OAuthRequest(Verb.GET, "http://example.com", - new OAuth20Service(null, new OAuthConfig("test", "test"))); + request = new OAuthRequest(Verb.GET, "http://example.com"); } @Test @@ -26,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 16b060000..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,74 +1,49 @@ package com.github.scribejava.core.model; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import org.junit.Before; import org.junit.Test; -import com.github.scribejava.core.oauth.OAuth20Service; -import com.github.scribejava.core.oauth.OAuthService; -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; - private ConnectionStub connection; - private OAuthService oAuthService; - - @Before - public void setUp() throws MalformedURLException { - connection = new ConnectionStub(); - oAuthService = new OAuth20Service(null, new OAuthConfig("test", "test")); - postRequest = new OAuthRequest(Verb.POST, "http://example.com", oAuthService); + @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"); - postRequest.setConnection(connection); - getRequest = new OAuthRequest(Verb.GET, "http://example.com?qsparam=value&other+param=value+with+spaces", - oAuthService); - getRequest.setConnection(connection); - } - @Test - public void shouldSetRequestVerb() { - getRequest.send(); - assertEquals("GET", connection.getRequestMethod()); - } + 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"))); } - @Test - public void shouldAddRequestHeaders() { - getRequest.addHeader("Header", "1"); - getRequest.addHeader("Header2", "2"); - getRequest.send(); - assertEquals(2, getRequest.getHeaders().size()); - assertEquals(2, connection.getHeaders().size()); - } - @Test public void shouldSetBodyParamsAndAddContentLength() { - assertEquals("param=value¶m%20with%20spaces=value%20with%20spaces", postRequest.getBodyContents()); - postRequest.send(); - assertTrue(connection.getHeaders().containsKey("Content-Length")); + 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() { - postRequest.addPayload("PAYLOAD"); - postRequest.send(); - assertEquals("PAYLOAD", postRequest.getBodyContents()); - assertTrue(connection.getHeaders().containsKey("Content-Length")); + 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()); } @Test public void shouldAllowAddingQuerystringParametersAfterCreation() { - final OAuthRequest request = new OAuthRequest(Verb.GET, "http://example.com?one=val", oAuthService); + final OAuthRequest request = new OAuthRequest(Verb.GET, "http://example.com?one=val"); request.addQuerystringParameter("two", "other val"); request.addQuerystringParameter("more", "params"); assertEquals(3, request.getQueryStringParams().size()); @@ -76,7 +51,7 @@ public void shouldAllowAddingQuerystringParametersAfterCreation() { @Test public void shouldReturnTheCompleteUrl() { - final OAuthRequest request = new OAuthRequest(Verb.GET, "http://example.com?one=val", oAuthService); + final OAuthRequest request = new OAuthRequest(Verb.GET, "http://example.com?one=val"); request.addQuerystringParameter("two", "other val"); request.addQuerystringParameter("more", "params"); assertEquals("http://example.com?one=val&two=other%20val&more=params", request.getCompleteUrl()); @@ -84,39 +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 shouldAutomaticallyAddContentTypeForPostRequestsWithBytePayload() { - postRequest.addPayload("PAYLOAD".getBytes()); - postRequest.send(); - assertEquals(OAuthRequest.DEFAULT_CONTENT_TYPE, connection.getHeaders().get("Content-Type")); - } + public void shouldNotEncodeInStringPayload() throws Exception { + final String requestBody = "~/!@#$%^&*( )_+//\r\n%2F&"; - @Test - public void shouldAutomaticallyAddContentTypeForPostRequestsWithStringPayload() { - postRequest.addPayload("PAYLOAD"); - postRequest.send(); - assertEquals(OAuthRequest.DEFAULT_CONTENT_TYPE, connection.getHeaders().get("Content-Type")); - } + final OAuthRequest postRequest = new OAuthRequest(Verb.POST, "http://example.com"); + postRequest.setPayload(requestBody); - @Test - public void shouldAutomaticallyAddContentTypeForPostRequestsWithBodyParameters() { - postRequest.send(); - assertEquals(OAuthRequest.DEFAULT_CONTENT_TYPE, connection.getHeaders().get("Content-Type")); + assertEquals(requestBody, postRequest.getStringPayload()); } @Test - public void shouldBeAbleToOverrideItsContentType() { - postRequest.addHeader("Content-Type", "my-content-type"); - postRequest.send(); - assertEquals("my-content-type", connection.getHeaders().get("Content-Type")); + 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 shouldNotAddContentTypeForGetRequests() { - getRequest.send(); - assertFalse(connection.getHeaders().containsKey("Content-Type")); + 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/model/ResponseTest.java b/scribejava-core/src/test/java/com/github/scribejava/core/model/ResponseTest.java deleted file mode 100644 index 1f7b0a4fe..000000000 --- a/scribejava-core/src/test/java/com/github/scribejava/core/model/ResponseTest.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.github.scribejava.core.model; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import static org.junit.Assert.assertEquals; -import org.junit.Before; -import org.junit.Test; - -public class ResponseTest { - - private Response response; - private ConnectionStub connection; - - @Before - public void setUp() throws IOException { - connection = new ConnectionStub(); - connection.addResponseHeader("one", "one"); - connection.addResponseHeader("two", "two"); - response = new Response(connection); - } - - @Test - public void shouldPopulateResponseHeaders() { - assertEquals(2, response.getHeaders().size()); - assertEquals("one", response.getHeader("one")); - } - - @Test - public void shouldParseBodyContents() throws IOException { - assertEquals("contents", response.getBody()); - assertEquals(1, connection.getTimesCalledInpuStream()); - } - - @Test - public void shouldParseBodyContentsOnlyOnce() throws IOException { - assertEquals("contents", response.getBody()); - assertEquals("contents", response.getBody()); - assertEquals("contents", response.getBody()); - assertEquals(1, connection.getTimesCalledInpuStream()); - } - - @Test - public void shouldHandleAConnectionWithErrors() throws IOException { - final Response errResponse = new Response(new FaultyConnection()); - assertEquals(400, errResponse.getCode()); - assertEquals("errors", errResponse.getBody()); - } - - private static class FaultyConnection extends ConnectionStub { - - private FaultyConnection() throws MalformedURLException { - super(); - } - - @Override - public InputStream getErrorStream() { - return new ByteArrayInputStream("errors".getBytes()); - } - - @Override - public int getResponseCode() throws IOException { - return 400; - } - } -} 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 7b23203ac..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,7 +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.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 { @@ -16,7 +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 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 050b0ae4d..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,85 +1,76 @@ 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 { - final OAuth20Service service = new ServiceBuilder() - .apiKey("your_api_key") + public void shouldProduceCorrectRequestSync() throws IOException, InterruptedException, ExecutionException { + final OAuth20Service service = new ServiceBuilder("your_api_key") .apiSecret("your_api_secret") .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 { - final OAuth20Service service = new ServiceBuilder() - .apiKey("your_api_key") + 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 public void testOAuthExtractAuthorization() { - final OAuth20Service service = new ServiceBuilder() - .apiKey("your_api_key") + final OAuth20Service service = new ServiceBuilder("your_api_key") .apiSecret("your_api_secret") .build(new OAuth20ApiUnit()); @@ -122,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 4d75c5ece..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,16 +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.model.AbstractRequest; +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.OAuthRequestAsync; 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; @@ -19,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 @@ -30,9 +35,8 @@ protected OAuth2AccessToken sendAccessTokenRequestSync(OAuthRequest request) { return new OAuth2AccessToken(TOKEN, prepareRawResponse(request)); } - private String prepareRawResponse(T request) { - final Gson json = new Gson(); - final Map response = new HashMap<>(); + private String prepareRawResponse(OAuthRequest request) { + final Map response = new HashMap<>(); response.put(OAuthConstants.ACCESS_TOKEN, TOKEN); response.put(OAuthConstants.STATE, STATE); response.put("expires_in", EXPIRES); @@ -40,15 +44,19 @@ private String prepareRawResponse(T 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 - protected Future sendAccessTokenRequestAsync(OAuthRequestAsync request, + protected Future sendAccessTokenRequestAsync(OAuthRequest request, OAuthAsyncRequestCallback callback) { final OAuth2AccessToken accessToken = new OAuth2AccessToken(TOKEN, prepareRawResponse(request)); 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 76d64c5ba..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,73 +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); - } - - @Test(expected = IllegalArgumentException.class) - public void shouldThrowExceptionForInvalidUrls() { - Preconditions.checkValidUrl("this/is/not/a/valid/url", ERROR_MSG); - } - - @Test(expected = IllegalArgumentException.class) - public void shouldThrowExceptionForNullUrls() { - Preconditions.checkValidUrl(null, ERROR_MSG); - } - - @Test - public void shouldAllowValidUrls() { - Preconditions.checkValidUrl("http://www.example.com", ERROR_MSG); - } - - @Test - public void shouldAllowSSLUrls() { - Preconditions.checkValidUrl("https://www.example.com", ERROR_MSG); - } - - @Test - public void shouldAllowSpecialCharsInScheme() { - Preconditions.checkValidUrl("custom+9.3-1://www.example.com", ERROR_MSG); - } - - @Test - public void shouldAllowNonStandarProtocolsForAndroid() { - Preconditions.checkValidUrl("x-url-custom://www.example.com", ERROR_MSG); - } - - @Test(expected = IllegalArgumentException.class) - public void shouldNotAllowStrangeProtocolNames() { - Preconditions.checkValidUrl("$weird*://www.example.com", ERROR_MSG); - } - - @Test(expected = IllegalArgumentException.class) - public void shouldNotAllowUnderscoreInScheme() { - Preconditions.checkValidUrl("http_custom://www.example.com", ERROR_MSG); - } - - @Test - public void shouldAllowOutOfBandAsValidCallbackValue() { - Preconditions.checkValidOAuthCallback("oob", 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 new file mode 100644 index 000000000..fabd225c3 --- /dev/null +++ b/scribejava-httpclient-ahc/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + + com.github.scribejava + scribejava + 8.3.4-SNAPSHOT + ../pom.xml + + + com.github.scribejava + scribejava-httpclient-ahc + ScribeJava Async Http Http Client support + jar + + + + com.github.scribejava + scribejava-core + ${project.version} + + + org.slf4j + slf4j-simple + 2.0.3 + test + + + org.asynchttpclient + async-http-client + 2.12.3 + + + 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-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 new file mode 100644 index 000000000..5c9b87249 --- /dev/null +++ b/scribejava-httpclient-ahc/src/main/java/com/github/scribejava/httpclient/ahc/AhcHttpClient.java @@ -0,0 +1,132 @@ +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; +import com.github.scribejava.core.model.Verb; +import org.asynchttpclient.AsyncHttpClient; +import org.asynchttpclient.DefaultAsyncHttpClient; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.Future; + +import java.io.File; +import org.asynchttpclient.AsyncHttpClientConfig; +import org.asynchttpclient.BoundRequestBuilder; + +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); + } + + public AhcHttpClient(AsyncHttpClient ahcClient) { + client = ahcClient; + } + + @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) { + return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, BodySetter.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("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) { + return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, BodySetter.STRING, bodyContents, callback, + converter); + } + + @Override + public Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, + File bodyContents, OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter) { + return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, BodySetter.FILE, bodyContents, callback, + converter); + } + + private Future doExecuteAsync(String userAgent, Map headers, Verb httpVerb, + String completeUrl, BodySetter bodySetter, Object bodyContents, OAuthAsyncRequestCallback callback, + OAuthRequest.ResponseConverter converter) { + final BoundRequestBuilder boundRequestBuilder; + switch (httpVerb) { + case GET: + boundRequestBuilder = client.prepareGet(completeUrl); + break; + case POST: + boundRequestBuilder = client.preparePost(completeUrl); + break; + case PUT: + boundRequestBuilder = client.preparePut(completeUrl); + break; + case DELETE: + boundRequestBuilder = client.prepareDelete(completeUrl); + break; + default: + throw new IllegalArgumentException("message build error: unknown verb type"); + } + + if (httpVerb.isPermitBody()) { + if (!headers.containsKey(CONTENT_TYPE)) { + boundRequestBuilder.addHeader(CONTENT_TYPE, DEFAULT_CONTENT_TYPE); + } + 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); + } + + return boundRequestBuilder.execute(new OAuthAsyncCompletionHandler<>(callback, converter)); + } + + private enum BodySetter { + BYTE_ARRAY { + @Override + BoundRequestBuilder setBody(BoundRequestBuilder requestBuilder, Object bodyContents) { + return requestBuilder.setBody((byte[]) bodyContents); + } + }, + STRING { + @Override + BoundRequestBuilder setBody(BoundRequestBuilder requestBuilder, Object bodyContents) { + return requestBuilder.setBody((String) bodyContents); + } + }, + FILE { + @Override + BoundRequestBuilder setBody(BoundRequestBuilder requestBuilder, Object bodyContents) { + return requestBuilder.setBody((File) bodyContents); + } + }; + + abstract BoundRequestBuilder setBody(BoundRequestBuilder requestBuilder, Object bodyContents); + } +} diff --git a/scribejava-httpclient-ahc/src/main/java/com/github/scribejava/httpclient/ahc/AhcHttpClientConfig.java b/scribejava-httpclient-ahc/src/main/java/com/github/scribejava/httpclient/ahc/AhcHttpClientConfig.java new file mode 100644 index 000000000..490eaf2e6 --- /dev/null +++ b/scribejava-httpclient-ahc/src/main/java/com/github/scribejava/httpclient/ahc/AhcHttpClientConfig.java @@ -0,0 +1,26 @@ +package com.github.scribejava.httpclient.ahc; + +import com.github.scribejava.core.httpclient.HttpClientConfig; +import org.asynchttpclient.AsyncHttpClientConfig; + +public class AhcHttpClientConfig implements HttpClientConfig { + + private final AsyncHttpClientConfig clientConfig; + + public AhcHttpClientConfig(AsyncHttpClientConfig clientConfig) { + this.clientConfig = clientConfig; + } + + public AsyncHttpClientConfig getClientConfig() { + return clientConfig; + } + + @Override + public AhcHttpClientConfig createDefaultConfig() { + return defaultConfig(); + } + + public static AhcHttpClientConfig defaultConfig() { + return new AhcHttpClientConfig(null); + } +} diff --git a/scribejava-httpclient-ahc/src/main/java/com/github/scribejava/httpclient/ahc/AhcProvider.java b/scribejava-httpclient-ahc/src/main/java/com/github/scribejava/httpclient/ahc/AhcProvider.java new file mode 100644 index 000000000..97c21dc86 --- /dev/null +++ b/scribejava-httpclient-ahc/src/main/java/com/github/scribejava/httpclient/ahc/AhcProvider.java @@ -0,0 +1,16 @@ +package com.github.scribejava.httpclient.ahc; + +import com.github.scribejava.core.httpclient.HttpClientProvider; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; + +public class AhcProvider implements HttpClientProvider { + + @Override + public HttpClient createClient(HttpClientConfig config) { + if (config instanceof AhcHttpClientConfig) { + return new AhcHttpClient((AhcHttpClientConfig) config); + } + return null; + } +} 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 new file mode 100644 index 000000000..0cb77af25 --- /dev/null +++ b/scribejava-httpclient-ahc/src/main/java/com/github/scribejava/httpclient/ahc/OAuthAsyncCompletionHandler.java @@ -0,0 +1,51 @@ +package com.github.scribejava.httpclient.ahc; + +import com.github.scribejava.core.model.OAuthAsyncRequestCallback; +import com.github.scribejava.core.model.OAuthRequest; +import com.github.scribejava.core.model.Response; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import org.asynchttpclient.AsyncCompletionHandler; + +public class OAuthAsyncCompletionHandler extends AsyncCompletionHandler { + + private final OAuthAsyncRequestCallback callback; + private final OAuthRequest.ResponseConverter converter; + + public OAuthAsyncCompletionHandler(OAuthAsyncRequestCallback callback, + OAuthRequest.ResponseConverter converter) { + this.callback = callback; + this.converter = converter; + } + + @Override + 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()); + } + + 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; + } + } + + @Override + public void onThrowable(Throwable t) { + if (callback != null) { + callback.onThrowable(t); + } + } +} diff --git a/scribejava-httpclient-ahc/src/main/resources/META-INF/services/com.github.scribejava.core.httpclient.HttpClientProvider b/scribejava-httpclient-ahc/src/main/resources/META-INF/services/com.github.scribejava.core.httpclient.HttpClientProvider new file mode 100644 index 000000000..bbe752ff6 --- /dev/null +++ b/scribejava-httpclient-ahc/src/main/resources/META-INF/services/com.github.scribejava.core.httpclient.HttpClientProvider @@ -0,0 +1 @@ +com.github.scribejava.httpclient.ahc.AhcProvider 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 new file mode 100644 index 000000000..39696d698 --- /dev/null +++ b/scribejava-httpclient-ning/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + + com.github.scribejava + scribejava + 8.3.4-SNAPSHOT + ../pom.xml + + + com.github.scribejava + scribejava-httpclient-ning + ScribeJava Ning Async Http Client support + jar + + + + com.github.scribejava + 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 + + + + + + + org.apache.felix + maven-bundle-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + + 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 new file mode 100644 index 000000000..52250f698 --- /dev/null +++ b/scribejava-httpclient-ning/src/main/java/com/github/scribejava/httpclient/ning/NingHttpClient.java @@ -0,0 +1,147 @@ +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; +import com.github.scribejava.core.model.Verb; +import com.ning.http.client.AsyncHttpClient; + +import java.util.Map; +import java.util.concurrent.Future; + +import com.ning.http.client.AsyncHttpClientConfig; +import java.io.File; + +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(); + if (ningAsyncHttpProviderClassName == null) { + client = config == null ? new AsyncHttpClient() : new AsyncHttpClient(config); + } else { + if (config == null) { + config = new AsyncHttpClientConfig.Builder().build(); + } + client = new AsyncHttpClient(ningAsyncHttpProviderClassName, config); + } + } + + public NingHttpClient(AsyncHttpClient client) { + this.client = client; + } + + @Override + public void close() { + client.close(); + } + + @Override + public Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, + final byte[] bodyContents, OAuthAsyncRequestCallback callback, + OAuthRequest.ResponseConverter converter) { + + return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, BodySetter.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("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); + } + + @Override + public Future executeAsync(String userAgent, Map headers, Verb httpVerb, String completeUrl, + final File bodyContents, OAuthAsyncRequestCallback callback, + OAuthRequest.ResponseConverter converter) { + + return doExecuteAsync(userAgent, headers, httpVerb, completeUrl, BodySetter.FILE, bodyContents, callback, + converter); + } + + private Future doExecuteAsync(String userAgent, Map headers, Verb httpVerb, + String completeUrl, BodySetter bodySetter, Object bodyContents, OAuthAsyncRequestCallback callback, + OAuthRequest.ResponseConverter converter) { + final AsyncHttpClient.BoundRequestBuilder boundRequestBuilder; + switch (httpVerb) { + case GET: + boundRequestBuilder = client.prepareGet(completeUrl); + break; + case POST: + boundRequestBuilder = client.preparePost(completeUrl); + break; + case PUT: + boundRequestBuilder = client.preparePut(completeUrl); + break; + case DELETE: + boundRequestBuilder = client.prepareDelete(completeUrl); + break; + default: + throw new IllegalArgumentException("message build error: unknown verb type"); + } + + if (httpVerb.isPermitBody()) { + if (!headers.containsKey(CONTENT_TYPE)) { + boundRequestBuilder.addHeader(CONTENT_TYPE, DEFAULT_CONTENT_TYPE); + } + 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); + } + + return boundRequestBuilder.execute(new OAuthAsyncCompletionHandler<>(callback, converter)); + } + + private enum BodySetter { + BYTE_ARRAY { + @Override + AsyncHttpClient.BoundRequestBuilder setBody(AsyncHttpClient.BoundRequestBuilder requestBuilder, + Object bodyContents) { + return requestBuilder.setBody((byte[]) bodyContents); + } + }, + STRING { + @Override + AsyncHttpClient.BoundRequestBuilder setBody(AsyncHttpClient.BoundRequestBuilder requestBuilder, + Object bodyContents) { + return requestBuilder.setBody((String) bodyContents); + } + }, + FILE { + @Override + AsyncHttpClient.BoundRequestBuilder setBody(AsyncHttpClient.BoundRequestBuilder requestBuilder, + Object bodyContents) { + return requestBuilder.setBody((File) bodyContents); + } + }; + + abstract AsyncHttpClient.BoundRequestBuilder setBody(AsyncHttpClient.BoundRequestBuilder requestBuilder, + Object bodyContents); + } +} 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 new file mode 100644 index 000000000..6e8842cc7 --- /dev/null +++ b/scribejava-httpclient-ning/src/main/java/com/github/scribejava/httpclient/ning/NingHttpClientConfig.java @@ -0,0 +1,40 @@ +package com.github.scribejava.httpclient.ning; + +import com.github.scribejava.core.httpclient.HttpClientConfig; +import com.ning.http.client.AsyncHttpClientConfig; + +public class NingHttpClientConfig implements HttpClientConfig { + + private final AsyncHttpClientConfig config; + private String ningAsyncHttpProviderClassName; + + public NingHttpClientConfig(AsyncHttpClientConfig config) { + this.config = config; + } + + public String getNingAsyncHttpProviderClassName() { + return ningAsyncHttpProviderClassName; + } + + public void setNingAsyncHttpProviderClassName(String ningAsyncHttpProviderClassName) { + this.ningAsyncHttpProviderClassName = ningAsyncHttpProviderClassName; + } + + public NingHttpClientConfig withNingAsyncHttpProviderClassName(String ningAsyncHttpProviderClassName) { + this.ningAsyncHttpProviderClassName = ningAsyncHttpProviderClassName; + return this; + } + + public AsyncHttpClientConfig getConfig() { + return config; + } + + @Override + public NingHttpClientConfig createDefaultConfig() { + return defaultConfig(); + } + + public static NingHttpClientConfig defaultConfig() { + return new NingHttpClientConfig(null); + } +} diff --git a/scribejava-httpclient-ning/src/main/java/com/github/scribejava/httpclient/ning/NingProvider.java b/scribejava-httpclient-ning/src/main/java/com/github/scribejava/httpclient/ning/NingProvider.java new file mode 100644 index 000000000..2db967249 --- /dev/null +++ b/scribejava-httpclient-ning/src/main/java/com/github/scribejava/httpclient/ning/NingProvider.java @@ -0,0 +1,16 @@ +package com.github.scribejava.httpclient.ning; + +import com.github.scribejava.core.httpclient.HttpClientProvider; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; + +public class NingProvider implements HttpClientProvider { + + @Override + public HttpClient createClient(HttpClientConfig httpClientConfig) { + if (httpClientConfig instanceof NingHttpClientConfig) { + return new NingHttpClient((NingHttpClientConfig) httpClientConfig); + } + return null; + } +} 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 new file mode 100644 index 000000000..cd8a25ce1 --- /dev/null +++ b/scribejava-httpclient-ning/src/main/java/com/github/scribejava/httpclient/ning/OAuthAsyncCompletionHandler.java @@ -0,0 +1,57 @@ +package com.github.scribejava.httpclient.ning; + +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.AsyncCompletionHandler; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class OAuthAsyncCompletionHandler extends AsyncCompletionHandler { + + private final OAuthAsyncRequestCallback callback; + private final OAuthRequest.ResponseConverter converter; + + public OAuthAsyncCompletionHandler(OAuthAsyncRequestCallback callback, + OAuthRequest.ResponseConverter converter) { + this.callback = callback; + this.converter = converter; + } + + @Override + 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()); + } + + 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; + } + } + + @Override + public void onThrowable(Throwable t) { + if (callback != null) { + callback.onThrowable(t); + } + } +} diff --git a/scribejava-httpclient-ning/src/main/resources/META-INF/services/com.github.scribejava.core.httpclient.HttpClientProvider b/scribejava-httpclient-ning/src/main/resources/META-INF/services/com.github.scribejava.core.httpclient.HttpClientProvider new file mode 100644 index 000000000..9f6843994 --- /dev/null +++ b/scribejava-httpclient-ning/src/main/resources/META-INF/services/com.github.scribejava.core.httpclient.HttpClientProvider @@ -0,0 +1 @@ +com.github.scribejava.httpclient.ning.NingProvider 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 new file mode 100644 index 000000000..59bc2bffc --- /dev/null +++ b/scribejava-httpclient-okhttp/pom.xml @@ -0,0 +1,49 @@ + + + 4.0.0 + + + com.github.scribejava + scribejava + 8.3.4-SNAPSHOT + ../pom.xml + + + com.github.scribejava + scribejava-httpclient-okhttp + ScribeJava Async OkHttp Client support + jar + + + + com.github.scribejava + scribejava-core + ${project.version} + + + com.squareup.okhttp3 + okhttp + 4.10.0 + + + 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-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 new file mode 100644 index 000000000..787b70395 --- /dev/null +++ b/scribejava-httpclient-okhttp/src/main/java/com/github/scribejava/httpclient/okhttp/OAuthAsyncCompletionHandler.java @@ -0,0 +1,58 @@ +package com.github.scribejava.httpclient.okhttp; + +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.Callback; + +import java.io.IOException; + +class OAuthAsyncCompletionHandler implements Callback { + + private final OAuthAsyncRequestCallback callback; + private final OAuthRequest.ResponseConverter converter; + private final OkHttpFuture okHttpFuture; + + OAuthAsyncCompletionHandler(OAuthAsyncRequestCallback callback, OAuthRequest.ResponseConverter converter, + OkHttpFuture okHttpFuture) { + this.callback = callback; + this.converter = converter; + this.okHttpFuture = okHttpFuture; + } + + @Override + public void onFailure(Call call, IOException exception) { + try { + okHttpFuture.setException(exception); + if (callback != null) { + callback.onThrowable(exception); + } + } finally { + okHttpFuture.finish(); + } + } + + @Override + public void onResponse(Call call, okhttp3.Response okHttpResponse) { + try { + + final Response response = OkHttpHttpClient.convertResponse(okHttpResponse); + 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 new file mode 100644 index 000000000..417b03e0a --- /dev/null +++ b/scribejava-httpclient-okhttp/src/main/java/com/github/scribejava/httpclient/okhttp/OkHttpFuture.java @@ -0,0 +1,70 @@ +package com.github.scribejava.httpclient.okhttp; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import okhttp3.Call; + +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; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + call.cancel(); + return call.isCanceled(); + } + + @Override + public boolean isCancelled() { + return call.isCanceled(); + } + + @Override + 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; + } + throw new TimeoutException(); + } + + void finish() { + latch.countDown(); + } + + void setResult(T result) { + this.result = result; + } + +} 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 new file mode 100644 index 000000000..f82697db0 --- /dev/null +++ b/scribejava-httpclient-okhttp/src/main/java/com/github/scribejava/httpclient/okhttp/OkHttpHttpClient.java @@ -0,0 +1,201 @@ +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; +import com.github.scribejava.core.model.Verb; +import okhttp3.Call; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +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; +import okhttp3.Headers; +import okhttp3.ResponseBody; + +public class OkHttpHttpClient implements HttpClient { + + private static final MediaType DEFAULT_CONTENT_TYPE_MEDIA_TYPE = MediaType.parse(DEFAULT_CONTENT_TYPE); + + 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(); + } + + public OkHttpHttpClient(OkHttpClient client) { + this.client = client; + } + + @Override + public void close() throws IOException { + client.dispatcher().executorService().shutdown(); + client.connectionPool().evictAll(); + final Cache cache = client.cache(); + if (cache != null) { + cache.close(); + } + } + + @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); + } + + @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); + } + + private Future doExecuteAsync(String userAgent, Map headers, Verb httpVerb, + String completeUrl, BodyType bodyType, Object bodyContents, OAuthAsyncRequestCallback callback, + OAuthRequest.ResponseConverter converter) { + final Call call = createCall(userAgent, headers, httpVerb, completeUrl, bodyType, bodyContents); + final OkHttpFuture okHttpFuture = new OkHttpFuture<>(call); + call.enqueue(new OAuthAsyncCompletionHandler<>(callback, converter, okHttpFuture)); + return okHttpFuture; + } + + @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); + } + + private Response doExecute(String userAgent, Map headers, Verb httpVerb, String completeUrl, + BodyType bodyType, Object bodyContents) throws IOException { + final Call call = createCall(userAgent, headers, httpVerb, completeUrl, bodyType, bodyContents); + return convertResponse(call.execute()); + } + + private Call createCall(String userAgent, Map headers, Verb httpVerb, String completeUrl, + BodyType bodyType, Object bodyContents) { + final Request.Builder requestBuilder = new Request.Builder(); + requestBuilder.url(completeUrl); + + final String method = httpVerb.name(); + + // prepare body + final RequestBody body; + if (bodyContents != null && HttpMethod.permitsRequestBody(method)) { + final MediaType mediaType = headers.containsKey(CONTENT_TYPE) ? MediaType.parse(headers.get(CONTENT_TYPE)) + : DEFAULT_CONTENT_TYPE_MEDIA_TYPE; + + body = bodyType.createBody(mediaType, bodyContents); + } else { + body = null; + } + + // fill HTTP method and body + requestBuilder.method(method, body); + + // fill headers + for (Map.Entry header : headers.entrySet()) { + requestBuilder.addHeader(header.getKey(), header.getValue()); + } + + if (userAgent != null) { + requestBuilder.header(OAuthConstants.USER_AGENT_HEADER_NAME, userAgent); + } + + // create a new call + return client.newCall(requestBuilder.build()); + } + + private enum BodyType { + BYTE_ARRAY { + @Override + RequestBody createBody(MediaType mediaType, Object bodyContents) { + return RequestBody.create((byte[]) bodyContents, mediaType); + } + }, + STRING { + @Override + RequestBody createBody(MediaType mediaType, Object bodyContents) { + return RequestBody.create((String) bodyContents, mediaType); + } + }, + FILE { + @Override + RequestBody createBody(MediaType mediaType, Object bodyContents) { + return RequestBody.create((File) bodyContents, mediaType); + } + }; + + abstract RequestBody createBody(MediaType mediaType, Object bodyContents); + } + + static Response convertResponse(okhttp3.Response okHttpResponse) { + final Headers headers = okHttpResponse.headers(); + final Map headersMap = new HashMap<>(); + for (String headerName : headers.names()) { + headersMap.put(headerName, headers.get(headerName)); + } + + final ResponseBody body = okHttpResponse.body(); + 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/main/java/com/github/scribejava/httpclient/okhttp/OkHttpHttpClientConfig.java b/scribejava-httpclient-okhttp/src/main/java/com/github/scribejava/httpclient/okhttp/OkHttpHttpClientConfig.java new file mode 100644 index 000000000..7759a0936 --- /dev/null +++ b/scribejava-httpclient-okhttp/src/main/java/com/github/scribejava/httpclient/okhttp/OkHttpHttpClientConfig.java @@ -0,0 +1,26 @@ +package com.github.scribejava.httpclient.okhttp; + +import com.github.scribejava.core.httpclient.HttpClientConfig; +import okhttp3.OkHttpClient; + +public class OkHttpHttpClientConfig implements HttpClientConfig { + + private final OkHttpClient.Builder clientBuilder; + + public OkHttpHttpClientConfig(OkHttpClient.Builder clientBuilder) { + this.clientBuilder = clientBuilder; + } + + public OkHttpClient.Builder getClientBuilder() { + return clientBuilder; + } + + @Override + public OkHttpHttpClientConfig createDefaultConfig() { + return defaultConfig(); + } + + public static OkHttpHttpClientConfig defaultConfig() { + return new OkHttpHttpClientConfig(null); + } +} diff --git a/scribejava-httpclient-okhttp/src/main/java/com/github/scribejava/httpclient/okhttp/OkHttpProvider.java b/scribejava-httpclient-okhttp/src/main/java/com/github/scribejava/httpclient/okhttp/OkHttpProvider.java new file mode 100644 index 000000000..4d27f5f0c --- /dev/null +++ b/scribejava-httpclient-okhttp/src/main/java/com/github/scribejava/httpclient/okhttp/OkHttpProvider.java @@ -0,0 +1,16 @@ +package com.github.scribejava.httpclient.okhttp; + +import com.github.scribejava.core.httpclient.HttpClientProvider; +import com.github.scribejava.core.httpclient.HttpClient; +import com.github.scribejava.core.httpclient.HttpClientConfig; + +public class OkHttpProvider implements HttpClientProvider { + + @Override + public HttpClient createClient(HttpClientConfig config) { + if (config instanceof OkHttpHttpClientConfig) { + return new OkHttpHttpClient((OkHttpHttpClientConfig) config); + } + return null; + } +} diff --git a/scribejava-httpclient-okhttp/src/main/resources/META-INF/services/com.github.scribejava.core.httpclient.HttpClientProvider b/scribejava-httpclient-okhttp/src/main/resources/META-INF/services/com.github.scribejava.core.httpclient.HttpClientProvider new file mode 100644 index 000000000..46afa18ed --- /dev/null +++ b/scribejava-httpclient-okhttp/src/main/resources/META-INF/services/com.github.scribejava.core.httpclient.HttpClientProvider @@ -0,0 +1 @@ +com.github.scribejava.httpclient.okhttp.OkHttpProvider 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 new file mode 100644 index 000000000..80fdf2834 --- /dev/null +++ b/scribejava-httpclient-okhttp/src/test/java/com/github/scribejava/httpclient/okhttp/OkHttpHttpClientTest.java @@ -0,0 +1,12 @@ +package com.github.scribejava.httpclient.okhttp; + +import com.github.scribejava.core.AbstractClientTest; +import com.github.scribejava.core.httpclient.HttpClient; + +public class OkHttpHttpClientTest extends AbstractClientTest { + + @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); + } + +}