From 5c0a394ccfcb4bcaa0c5e6d64ca0a1af054424a9 Mon Sep 17 00:00:00 2001 From: Peter Boling Date: 2022年8月30日 18:34:53 +0700 Subject: [PATCH 1/7] =?UTF-8?q?=F0=9F=93=9D=20Document=20migration=20to=20?= =?UTF-8?q?Gitlab?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 539 ++---------------------------------------------------- 1 file changed, 12 insertions(+), 527 deletions(-) diff --git a/README.md b/README.md index 83dc1163..2b8e5e94 100644 --- a/README.md +++ b/README.md @@ -7,533 +7,18 @@

-## What +# Ruby OAuth 2 has moved to GitLab -OAuth 2.0 is the industry-standard protocol for authorization. -OAuth 2.0 focuses on client developer simplicity while providing specific authorization flows for web applications, - desktop applications, mobile phones, and living room devices. -This is a RubyGem for implementing OAuth 2.0 clients and servers in Ruby applications. -See the sibling `oauth` gem for OAuth 1.0 implementations in Ruby. +Please update references: https://gitlab.com/oauth-xx/oauth2/ ---- +* Source Code: [![Gitlab](https://img.shields.io/badge/source-gitlab-brightgreen.svg?style=flat)][source] +* RubyDoc Documentation: [![RubyDoc.info](https://img.shields.io/badge/documentation-rubydoc-brightgreen.svg?style=flat)][documentation] +* Mailing List/Google Group: [![Mailing List](https://img.shields.io/badge/group-mailinglist-violet.svg?style=social&logo=google)][mailinglist] +* Live Chat on Gitter: [![Join the chat at https://gitter.im/oauth-xx/oauth-ruby](https://badges.gitter.im/Join%20Chat.svg)][chat] +* Maintainer's Blog: [![Blog](https://img.shields.io/badge/blog-railsbling-brightgreen.svg?style=flat)][blogpage] -* [OAuth 2.0 Spec][oauth2-spec] -* [oauth sibling gem][sibling-gem] for OAuth 1.0 implementations in Ruby. - -[oauth2-spec]: https://oauth.net/2/ -[sibling-gem]: https://github.com/oauth-xx/oauth-ruby - -## Release Documentation - -### Version 2.0.x - -
- 2.0.x Readmes - -| Version | Release Date | Readme | -|---------|--------------|----------------------------------------------------------| -| 2.0.7 | 2022年08月22日 | https://github.com/oauth-xx/oauth2/blob/v2.0.7/README.md | -| 2.0.6 | 2022年07月13日 | https://github.com/oauth-xx/oauth2/blob/v2.0.6/README.md | -| 2.0.5 | 2022年07月07日 | https://github.com/oauth-xx/oauth2/blob/v2.0.5/README.md | -| 2.0.4 | 2022年07月01日 | https://github.com/oauth-xx/oauth2/blob/v2.0.4/README.md | -| 2.0.3 | 2022年06月28日 | https://github.com/oauth-xx/oauth2/blob/v2.0.3/README.md | -| 2.0.2 | 2022年06月24日 | https://github.com/oauth-xx/oauth2/blob/v2.0.2/README.md | -| 2.0.1 | 2022年06月22日 | https://github.com/oauth-xx/oauth2/blob/v2.0.1/README.md | -| 2.0.0 | 2022年06月21日 | https://github.com/oauth-xx/oauth2/blob/v2.0.0/README.md | -
- -### Older Releases - -
- 1.4.x Readmes - -| Version | Release Date | Readme | -|---------|--------------|-----------------------------------------------------------| -| 1.4.10 | Jul 1, 2022 | https://github.com/oauth-xx/oauth2/blob/v1.4.10/README.md | -| 1.4.9 | Feb 20, 2022 | https://github.com/oauth-xx/oauth2/blob/v1.4.9/README.md | -| 1.4.8 | Feb 18, 2022 | https://github.com/oauth-xx/oauth2/blob/v1.4.8/README.md | -| 1.4.7 | Mar 19, 2021 | https://github.com/oauth-xx/oauth2/blob/v1.4.7/README.md | -| 1.4.6 | Mar 19, 2021 | https://github.com/oauth-xx/oauth2/blob/v1.4.6/README.md | -| 1.4.5 | Mar 18, 2021 | https://github.com/oauth-xx/oauth2/blob/v1.4.5/README.md | -| 1.4.4 | Feb 12, 2020 | https://github.com/oauth-xx/oauth2/blob/v1.4.4/README.md | -| 1.4.3 | Jan 29, 2020 | https://github.com/oauth-xx/oauth2/blob/v1.4.3/README.md | -| 1.4.2 | Oct 1, 2019 | https://github.com/oauth-xx/oauth2/blob/v1.4.2/README.md | -| 1.4.1 | Oct 13, 2018 | https://github.com/oauth-xx/oauth2/blob/v1.4.1/README.md | -| 1.4.0 | Jun 9, 2017 | https://github.com/oauth-xx/oauth2/blob/v1.4.0/README.md | -
- -
- 1.3.x Readmes - -| Version | Release Date | Readme | -|----------|--------------|----------------------------------------------------------| -| 1.3.1 | Mar 3, 2017 | https://github.com/oauth-xx/oauth2/blob/v1.3.1/README.md | -| 1.3.0 | Dec 27, 2016 | https://github.com/oauth-xx/oauth2/blob/v1.3.0/README.md | -
- -
- ≤= 1.2.x Readmes (2016 and before) - -| Version | Release Date | Readme | -|----------|--------------|----------------------------------------------------------| -| 1.2.0 | Jun 30, 2016 | https://github.com/oauth-xx/oauth2/blob/v1.2.0/README.md | -| 1.1.0 | Jan 30, 2016 | https://github.com/oauth-xx/oauth2/blob/v1.1.0/README.md | -| 1.0.0 | May 23, 2014 | https://github.com/oauth-xx/oauth2/blob/v1.0.0/README.md | -| < 1.0.0 | Find here | https://github.com/oauth-xx/oauth2/tags | -
- -## Status - - - -| | Project | bundle add oauth2 | -|:----|-----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| 1️⃣ | name, license, docs | [![RubyGems.org][⛳️name-img]][⛳️gem] [![License: MIT][🖇src-license-img]][🖇src-license] [![FOSSA][🏘fossa-img]][🏘fossa] [![RubyDoc.info][🚎yard-img]][🚎yard] [![InchCI][🖐inch-ci-img]][🚎yard] | -| 2️⃣ | version & activity | [![Gem Version][⛳️version-img]][⛳️gem] [![Total Downloads][🖇DL-total-img]][⛳️gem] [![Download Rank][🏘DL-rank-img]][⛳️gem] [![Source Code][🚎src-home-img]][🚎src-home] [![Open PRs][🖐prs-o-img]][🖐prs-o] [![Closed PRs][🧮prs-c-img]][🧮prs-c] [![Next Version][📗next-img♻️]][📗next♻️] | -| 3️⃣ | maintanence & linting | [![Maintainability][⛳cclim-maint-img♻️]][⛳cclim-maint] [![Helpers][🖇triage-help-img]][🖇triage-help] [![Depfu][🏘depfu-img♻️]][🏘depfu♻️] [![Contributors][🚎contributors-img]][🚎contributors] [![Style][🖐style-wf-img]][🖐style-wf] [![Kloc Roll][🧮kloc-img]][🧮kloc] | -| 4️⃣ | testing | [![Open Issues][⛳iss-o-img]][⛳iss-o] [![Closed Issues][🖇iss-c-img]][🖇iss-c] [![Supported][🏘sup-wf-img]][🏘sup-wf] [![Heads][🚎heads-wf-img]][🚎heads-wf] [![Unofficial Support][🖐uns-wf-img]][🖐uns-wf] [![MacOS][🧮mac-wf-img]][🧮mac-wf] [![Windows][📗win-wf-img]][📗win-wf] | -| 5️⃣ | coverage & security | [![CodeClimate][⛳cclim-cov-img♻️]][⛳cclim-cov] [![CodeCov][🖇codecov-img♻️]][🖇codecov] [![Coveralls][🏘coveralls-img]][🏘coveralls] [![Security Policy][🚎sec-pol-img]][🚎sec-pol] [![CodeQL][🖐codeQL-img]][🖐codeQL] [![Code Coverage][🧮cov-wf-img]][🧮cov-wf] | -| 6️⃣ | resources | [![Discussion][⛳gh-discussions-img]][⛳gh-discussions] [![Get help on Codementor][🖇codementor-img]][🖇codementor] [![Chat][🏘chat-img]][🏘chat] [![Blog][🚎blog-img]][🚎blog] [![Blog][🖐wiki-img]][🖐wiki] | -| 7️⃣ | spread 💖 | [![Liberapay Patrons][⛳liberapay-img]][⛳liberapay] [![Sponsor Me][🖇sponsor-img]][🖇sponsor] [![Tweet @ Peter][🏘tweet-img]][🏘tweet] [🌏][aboutme] [👼][angelme] [💻][coderme] | - - - - -[⛳️gem]: https://rubygems.org/gems/oauth2 -[⛳️name-img]: https://img.shields.io/badge/name-oauth2-brightgreen.svg?style=flat -[🖇src-license]: https://opensource.org/licenses/MIT -[🖇src-license-img]: https://img.shields.io/badge/License-MIT-green.svg -[🏘fossa]: https://app.fossa.io/projects/git%2Bgithub.com%2Foauth-xx%2Foauth2?ref=badge_shield -[🏘fossa-img]: https://app.fossa.io/api/projects/git%2Bgithub.com%2Foauth-xx%2Foauth2.svg?type=shield -[🚎yard]: https://www.rubydoc.info/github/oauth-xx/oauth2 -[🚎yard-img]: https://img.shields.io/badge/documentation-rubydoc-brightgreen.svg?style=flat -[🖐inch-ci-img]: http://inch-ci.org/github/oauth-xx/oauth2.png - - -[⛳️version-img]: http://img.shields.io/gem/v/oauth2.svg -[🖇DL-total-img]: https://img.shields.io/gem/dt/oauth2.svg -[🏘DL-rank-img]: https://img.shields.io/gem/rt/oauth2.svg -[🚎src-home]: https://github.com/oauth-xx/oauth2 -[🚎src-home-img]: https://img.shields.io/badge/source-github-brightgreen.svg?style=flat -[🖐prs-o]: https://github.com/oauth-xx/oauth2/pulls -[🖐prs-o-img]: https://img.shields.io/github/issues-pr/oauth-xx/oauth2 -[🧮prs-c]: https://github.com/oauth-xx/oauth2/pulls?q=is%3Apr+is%3Aclosed -[🧮prs-c-img]: https://img.shields.io/github/issues-pr-closed/oauth-xx/oauth2 -[📗next♻️]: https://github.com/oauth-xx/oauth2/milestone/2 -[📗next-img♻️]: https://img.shields.io/github/milestones/progress/oauth-xx/oauth2/2?label=Next%20Version - - -[⛳cclim-maint]: https://codeclimate.com/github/oauth-xx/oauth2/maintainability -[⛳cclim-maint-img♻️]: https://api.codeclimate.com/v1/badges/688c612528ff90a46955/maintainability -[🖇triage-help]: https://www.codetriage.com/oauth-xx/oauth2 -[🖇triage-help-img]: https://www.codetriage.com/oauth-xx/oauth2/badges/users.svg -[🏘depfu♻️]: https://depfu.com/github/oauth-xx/oauth2?project_id=4445 -[🏘depfu-img♻️]: https://badges.depfu.com/badges/6d34dc1ba682bbdf9ae2a97848241743/count.svg -[🚎contributors]: https://github.com/oauth-xx/oauth2/graphs/contributors -[🚎contributors-img]: https://img.shields.io/github/contributors-anon/oauth-xx/oauth2 -[🖐style-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/style.yml -[🖐style-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/style.yml/badge.svg -[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ -[🧮kloc-img]: https://img.shields.io/tokei/lines/github.com/oauth-xx/oauth2 - - -[⛳iss-o]: https://github.com/oauth-xx/oauth2/issues -[⛳iss-o-img]: https://img.shields.io/github/issues-raw/oauth-xx/oauth2 -[🖇iss-c]: https://github.com/oauth-xx/oauth2/issues?q=is%3Aissue+is%3Aclosed -[🖇iss-c-img]: https://img.shields.io/github/issues-closed-raw/oauth-xx/oauth2 -[🏘sup-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/supported.yml -[🏘sup-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/supported.yml/badge.svg -[🚎heads-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/heads.yml -[🚎heads-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/heads.yml/badge.svg -[🖐uns-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/unsupported.yml -[🖐uns-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/unsupported.yml/badge.svg -[🧮mac-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/macos.yml -[🧮mac-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/macos.yml/badge.svg -[📗win-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/windows.yml -[📗win-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/windows.yml/badge.svg - - -[⛳cclim-cov]: https://codeclimate.com/github/oauth-xx/oauth2/test_coverage -[⛳cclim-cov-img♻️]: https://api.codeclimate.com/v1/badges/688c612528ff90a46955/test_coverage -[🖇codecov-img♻️]: https://codecov.io/gh/oauth-xx/oauth2/branch/master/graph/badge.svg?token=bNqSzNiuo2 -[🖇codecov]: https://codecov.io/gh/oauth-xx/oauth2 -[🏘coveralls]: https://coveralls.io/github/oauth-xx/oauth2?branch=master -[🏘coveralls-img]: https://coveralls.io/repos/github/oauth-xx/oauth2/badge.svg?branch=master -[🚎sec-pol]: https://github.com/oauth-xx/oauth2/blob/master/SECURITY.md -[🚎sec-pol-img]: https://img.shields.io/badge/security-policy-brightgreen.svg?style=flat -[🖐codeQL]: https://github.com/oauth-xx/oauth2/security/code-scanning -[🖐codeQL-img]: https://github.com/oauth-xx/oauth2/actions/workflows/codeql-analysis.yml/badge.svg -[🧮cov-wf]: https://github.com/oauth-xx/oauth2/actions/workflows/coverage.yml -[🧮cov-wf-img]: https://github.com/oauth-xx/oauth2/actions/workflows/coverage.yml/badge.svg - - -[⛳gh-discussions]: https://github.com/oauth-xx/oauth2/discussions -[⛳gh-discussions-img]: https://img.shields.io/github/discussions/oauth-xx/oauth2 -[🖇codementor]: https://www.codementor.io/peterboling?utm_source=github&utm_medium=button&utm_term=peterboling&utm_campaign=github -[🖇codementor-img]: https://cdn.codementor.io/badges/get_help_github.svg -[🏘chat]: https://gitter.im/oauth-xx/oauth2 -[🏘chat-img]: https://img.shields.io/gitter/room/oauth-xx/oauth2.svg -[🚎blog]: http://www.railsbling.com/tags/oauth2/ -[🚎blog-img]: https://img.shields.io/badge/blog-railsbling-brightgreen.svg?style=flat -[🖐wiki]: https://github.com/oauth-xx/oauth2/wiki -[🖐wiki-img]: https://img.shields.io/badge/wiki-examples-brightgreen.svg?style=flat - - -[⛳liberapay-img]: https://img.shields.io/liberapay/patrons/pboling.svg?logo=liberapay -[⛳liberapay]: https://liberapay.com/pboling/donate -[🖇sponsor-img]: https://img.shields.io/badge/sponsor-pboling.svg?style=social&logo=github -[🖇sponsor]: https://github.com/sponsors/pboling -[🏘tweet-img]: https://img.shields.io/twitter/follow/galtzo.svg?style=social&label=Follow -[🏘tweet]: http://twitter.com/galtzo - - -[railsbling]: http://www.railsbling.com -[peterboling]: http://www.peterboling.com -[aboutme]: https://about.me/peter.boling -[angelme]: https://angel.co/peter-boling -[coderme]:http://coderwall.com/pboling - -## Installation - -Install the gem and add to the application's Gemfile by executing: - - $ bundle add oauth2 - -If bundler is not being used to manage dependencies, install the gem by executing: - - $ gem install oauth2 - -## OAuth2 for Enterprise - -Available as part of the Tidelift Subscription. - -The maintainers of OAuth2 and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. [Learn more.][tidelift-ref] - -[tidelift-ref]: https://tidelift.com/subscription/pkg/rubygems-oauth2?utm_source=rubygems-oauth2&utm_medium=referral&utm_campaign=enterprise - -## Security contact information - -To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). -Tidelift will coordinate the fix and disclosure. - -For more see [SECURITY.md][🚎sec-pol]. - -## What is new for v2.0? - -- Officially support Ruby versions>= 2.7 -- Unofficially support Ruby versions>= 2.5 -- Incidentally support Ruby versions>= 2.2 -- Drop support for the expired MAC Draft (all versions) -- Support IETF rfc7523 JWT Bearer Tokens -- Support IETF rfc7231 Relative Location in Redirect -- Support IETF rfc6749 Don't set oauth params when nil -- Support [OIDC 1.0 Private Key JWT](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication); based on the OAuth JWT assertion specification [(RFC 7523)](https://tools.ietf.org/html/rfc7523) -- Support new formats, including from [jsonapi.org](http://jsonapi.org/format/): `application/vdn.api+json`, `application/vnd.collection+json`, `application/hal+json`, `application/problem+json` -- Adds new option to `OAuth2::Client#get_token`: - - `:access_token_class` (`AccessToken`); user specified class to use for all calls to `get_token` -- Adds new option to `OAuth2::AccessToken#initialize`: - - `:expires_latency` (`nil`); number of seconds by which AccessToken validity will be reduced to offset latency -- By default, keys are transformed to camel case. - - Original keys will still work as previously, in most scenarios, thanks to `rash_alt` gem. - - However, this is a _breaking_ change if you rely on `response.parsed.to_h`, as the keys in the result will be camel case. - - As of version 2.0.4 you can turn key transformation off with the `snaky: false` option. -- By default, the `:auth_scheme` is now `:basic_auth` (instead of `:request_body`) - - Third-party strategies and gems may need to be updated if a provider was requiring client id/secret in the request body -- [... A lot more](https://github.com/oauth-xx/oauth2/blob/master/CHANGELOG.md#2.0.0) - -## Compatibility - -Targeted ruby compatibility is non-EOL versions of Ruby, currently 2.7, 3.0 and -3.1. Compatibility is further distinguished by supported and unsupported versions of Ruby. -Ruby is limited to 2.2+ for 2.x releases. See `1-4-stable` branch for older rubies. - -
- Ruby Engine Compatibility Policy - -This gem is tested against MRI, JRuby, and Truffleruby. -Each of those has varying versions that target a specific version of MRI Ruby. -This gem should work in the just-listed Ruby engines according to the targeted MRI compatibility in the table below. -If you would like to add support for additional engines, - first make sure Github Actions supports the engine, - then submit a PR to the correct maintenance branch as according to the table below. -
- -
- Ruby Version Compatibility Policy - -If something doesn't work on one of these interpreters, it's a bug. - -This library may inadvertently work (or seem to work) on other Ruby -implementations, however support will only be provided for the versions listed -above. - -If you would like this library to support another Ruby version, you may -volunteer to be a maintainer. Being a maintainer entails making sure all tests -run and pass on that implementation. When something breaks on your -implementation, you will be responsible for providing patches in a timely -fashion. If critical issues for a particular implementation exist at the time -of a major release, support for that Ruby version may be dropped. -
- -| | Ruby OAuth2 Version | Maintenance Branch | Supported Officially | Supported Unofficially | Supported Incidentally | -|:----|---------------------|--------------------|-------------------------|------------------------|------------------------| -| 1️⃣ | 2.0.x | `master` | 2.7, 3.0, 3.1 | 2.5, 2.6 | 2.2, 2.3, 2.4 | -| 2️⃣ | 1.4.x | `1-4-stable` | 2.5, 2.6, 2.7, 3.0, 3.1 | 2.1, 2.2, 2.3, 2.4 | 1.9, 2.0 | -| 3️⃣ | older | N/A | Best of luck to you! | Please upgrade! | | - -NOTE: The 1.4 series will only receive critical security updates. -See [SECURITY.md][🚎sec-pol] - -## Usage Examples - -### `authorize_url` and `token_url` are on site root (Just Works!) - -```ruby -require 'oauth2' -client = OAuth2::Client.new('client_id', 'client_secret', site: 'https://example.org') -# => # 'Basic some_password'}) -response = access.get('/api/resource', params: {'query_foo' => 'bar'}) -response.class.name -# => OAuth2::Response -``` - -### Relative `authorize_url` and `token_url` (Not on site root, Just Works!) - -In above example, the default Authorization URL is `oauth/authorize` and default Access Token URL is `oauth/token`, and, as they are missing a leading `/`, both are relative. - -```ruby -client = OAuth2::Client.new('client_id', 'client_secret', site: 'https://example.org/nested/directory/on/your/server') -# => # # OAuth2::Client -``` - -### snake_case and indifferent access in Response#parsed - -```ruby -response = access.get('/api/resource', params: {'query_foo' => 'bar'}) -# Even if the actual response is CamelCase. it will be made available as snaky: -JSON.parse(response.body) # => {"accessToken"=>"aaaaaaaa", "additionalData"=>"additional"} -response.parsed # => {"access_token"=>"aaaaaaaa", "additional_data"=>"additional"} -response.parsed.access_token # => "aaaaaaaa" -response.parsed[:access_token] # => "aaaaaaaa" -response.parsed.additional_data # => "additional" -response.parsed[:additional_data] # => "additional" -response.parsed.class.name # => OAuth2::SnakyHash (subclass of Hashie::Mash::Rash, from `rash_alt` gem) -``` - -#### What if I hate snakes and/or indifference? - -```ruby -response = access.get('/api/resource', params: {'query_foo' => 'bar'}, snaky: false) -JSON.parse(response.body) # => {"accessToken"=>"aaaaaaaa", "additionalData"=>"additional"} -response.parsed # => {"accessToken"=>"aaaaaaaa", "additionalData"=>"additional"} -response.parsed['accessToken'] # => "aaaaaaaa" -response.parsed['additionalData'] # => "additional" -response.parsed.class.name # => Hash (just, regular old Hash) -``` - -
- Debugging - -Set an environment variable, however you would [normally do that](https://github.com/bkeepers/dotenv). - -```ruby -# will log both request and response, including bodies -ENV['OAUTH_DEBUG'] = 'true' -``` - -By default, debug output will go to `$stdout`. This can be overridden when -initializing your OAuth2::Client. - -```ruby -require 'oauth2' -client = OAuth2::Client.new( - 'client_id', - 'client_secret', - site: 'https://example.org', - logger: Logger.new('example.log', 'weekly') -) -``` -
- -## OAuth2::Response - -The `AccessToken` methods `#get`, `#post`, `#put` and `#delete` and the generic `#request` -will return an instance of the #OAuth2::Response class. - -This instance contains a `#parsed` method that will parse the response body and -return a Hash-like [`OAuth2::SnakyHash`](https://github.com/oauth-xx/oauth2/blob/master/lib/oauth2/snaky_hash.rb) if the `Content-Type` is `application/x-www-form-urlencoded` or if -the body is a JSON object. It will return an Array if the body is a JSON -array. Otherwise, it will return the original body string. - -The original response body, headers, and status can be accessed via their -respective methods. - -## OAuth2::AccessToken - -If you have an existing Access Token for a user, you can initialize an instance -using various class methods including the standard new, `from_hash` (if you have -a hash of the values), or `from_kvform` (if you have an -`application/x-www-form-urlencoded` encoded string of the values). - -## OAuth2::Error - -On 400+ status code responses, an `OAuth2::Error` will be raised. If it is a -standard OAuth2 error response, the body will be parsed and `#code` and `#description` will contain the values provided from the error and -`error_description` parameters. The `#response` property of `OAuth2::Error` will -always contain the `OAuth2::Response` instance. - -If you do not want an error to be raised, you may use `:raise_errors => false` -option on initialization of the client. In this case the `OAuth2::Response` -instance will be returned as usual and on 400+ status code responses, the -Response instance will contain the `OAuth2::Error` instance. - -## Authorization Grants - -Currently the Authorization Code, Implicit, Resource Owner Password Credentials, Client Credentials, and Assertion -authentication grant types have helper strategy classes that simplify client -use. They are available via the [`#auth_code`](https://github.com/oauth-xx/oauth2/blob/master/lib/oauth2/strategy/auth_code.rb), [`#implicit`](https://github.com/oauth-xx/oauth2/blob/master/lib/oauth2/strategy/implicit.rb), [`#password`](https://github.com/oauth-xx/oauth2/blob/master/lib/oauth2/strategy/password.rb), [`#client_credentials`](https://github.com/oauth-xx/oauth2/blob/master/lib/oauth2/strategy/client_credentials.rb), and [`#assertion`](https://github.com/oauth-xx/oauth2/blob/master/lib/oauth2/strategy/assertion.rb) methods respectively. - -These aren't full examples, but demonstrative of the differences between usage for each strategy. -```ruby -auth_url = client.auth_code.authorize_url(redirect_uri: 'http://localhost:8080/oauth/callback') -access = client.auth_code.get_token('code_value', redirect_uri: 'http://localhost:8080/oauth/callback') - -auth_url = client.implicit.authorize_url(redirect_uri: 'http://localhost:8080/oauth/callback') -# get the token params in the callback and -access = OAuth2::AccessToken.from_kvform(client, query_string) - -access = client.password.get_token('username', 'password') - -access = client.client_credentials.get_token - -# Client Assertion Strategy -# see: https://tools.ietf.org/html/rfc7523 -claimset = { - iss: 'http://localhost:3001', - aud: 'http://localhost:8080/oauth2/token', - sub: 'me@example.com', - exp: Time.now.utc.to_i + 3600, -} -assertion_params = [claimset, 'HS256', 'secret_key'] -access = client.assertion.get_token(assertion_params) - -# The `access` (i.e. access token) is then used like so: -access.token # actual access_token string, if you need it somewhere -access.get('/api/stuff') # making api calls with access token -``` - -If you want to specify additional headers to be sent out with the -request, add a 'headers' hash under 'params': - -```ruby -access = client.auth_code.get_token('code_value', redirect_uri: 'http://localhost:8080/oauth/callback', headers: {'Some' => 'Header'}) -``` - -You can always use the `#request` method on the `OAuth2::Client` instance to make -requests for tokens for any Authentication grant type. - -## Versioning - -This library aims to adhere to [Semantic Versioning 2.0.0][semver]. -Violations of this scheme should be reported as bugs. Specifically, -if a minor or patch version is released that breaks backward -compatibility, a new version should be immediately released that -restores compatibility. Breaking changes to the public API will -only be introduced with new major versions. - -As a result of this policy, you can (and should) specify a -dependency on this gem using the [Pessimistic Version Constraint][pvc] with two digits of precision. - -For example: - -```ruby -spec.add_dependency 'oauth2', '~> 2.0' -``` - -[semver]: http://semver.org/ -[pvc]: http://guides.rubygems.org/patterns/#pessimistic-version-constraint - -## License - -[![License: MIT][🖇src-license-img]][🖇src-license] - -- Copyright (c) 2011-2013 Michael Bleigh and Intridea, Inc. -- Copyright (c) 2017-2022 [oauth-xx organization][oauth-xx] -- See [LICENSE][license] for details. - -[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Foauth-xx%2Foauth2.svg?type=large)][fossa2] - -[license]: https://github.com/oauth-xx/oauth2/blob/master/LICENSE -[oauth-xx]: https://github.com/oauth-xx -[fossa2]: https://app.fossa.io/projects/git%2Bgithub.com%2Foauth-xx%2Foauth2?ref=badge_large - -## Development - -After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. - -To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). - -## Contributing - -See [CONTRIBUTING.md][contributing] - -[contributing]: https://github.com/oauth-xx/oauth2/blob/master/CONTRIBUTING.md - -## Contributors - -[![Contributors](https://contrib.rocks/image?repo=oauth-xx/oauth2)]("https://github.com/oauth-xx/oauth2/graphs/contributors") - -Made with [contributors-img](https://contrib.rocks). - -## Code of Conduct - -Everyone interacting in the OAuth2 project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/oauth-xx/oauth2/blob/master/CODE_OF_CONDUCT.md). +[documentation]: https://rubydoc.info/github/oauth-xx/oauth2 +[mailinglist]: http://groups.google.com/group/oauth-ruby +[chat]: https://gitter.im/oauth-xx/oauth2?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge +[blogpage]: http://www.railsbling.com/tags/oauth2/ +[source]: https://gitlab.com/oauth-xx/oauth2/ From dcc0301555617209f1ab1119ebba513c05908cde Mon Sep 17 00:00:00 2001 From: Peter Boling Date: 2022年8月30日 18:36:05 +0700 Subject: [PATCH 2/7] =?UTF-8?q?=F0=9F=93=9D=20Document=20how=20to=20update?= =?UTF-8?q?=20local?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 2b8e5e94..2a18c192 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,11 @@ Please update references: https://gitlab.com/oauth-xx/oauth2/ +If you have the project checked out, simply: +```bash +git remote set-url origin git@gitlab.com:oauth-xx/oauth2.git +``` + * Source Code: [![Gitlab](https://img.shields.io/badge/source-gitlab-brightgreen.svg?style=flat)][source] * RubyDoc Documentation: [![RubyDoc.info](https://img.shields.io/badge/documentation-rubydoc-brightgreen.svg?style=flat)][documentation] * Mailing List/Google Group: [![Mailing List](https://img.shields.io/badge/group-mailinglist-violet.svg?style=social&logo=google)][mailinglist] From 7469a188ac2f54779b6cc5c73c7953b2295134d8 Mon Sep 17 00:00:00 2001 From: Peter Boling Date: 2022年9月15日 07:02:09 +0700 Subject: [PATCH 3/7] =?UTF-8?q?=F0=9F=93=9D=20Document=20migration=20to=20?= =?UTF-8?q?Gitlab?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 2a18c192..80766325 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,10 @@ If you have the project checked out, simply: git remote set-url origin git@gitlab.com:oauth-xx/oauth2.git ``` +We also took the opportunity of the migration to switch the default branch from `master` to `main`. + +Changes are still being pushed back to the Github branches `1-4-stable` and `main`, but please update dependencies to reference released versions or the new Gitlab source host. + * Source Code: [![Gitlab](https://img.shields.io/badge/source-gitlab-brightgreen.svg?style=flat)][source] * RubyDoc Documentation: [![RubyDoc.info](https://img.shields.io/badge/documentation-rubydoc-brightgreen.svg?style=flat)][documentation] * Mailing List/Google Group: [![Mailing List](https://img.shields.io/badge/group-mailinglist-violet.svg?style=social&logo=google)][mailinglist] From c4c9c1df322667ee4f82d0ce47c83014160d5644 Mon Sep 17 00:00:00 2001 From: Peter Boling Date: 2022年9月15日 07:04:25 +0700 Subject: [PATCH 4/7] =?UTF-8?q?=F0=9F=94=A5=20Branch=20is=20Gitlab=20migra?= =?UTF-8?q?tion=20documentation=20only?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/FUNDING.yml | 13 - .github/dependabot.yml | 10 - .github/workflows/codeql-analysis.yml | 70 -- .github/workflows/coverage.yml | 115 --- .github/workflows/danger.yml | 45 - .github/workflows/heads.yml | 70 -- .github/workflows/jruby-head.yml | 69 -- .github/workflows/macos-ancient.yml | 58 -- .github/workflows/macos.yml | 61 -- .github/workflows/style.yml | 43 - .github/workflows/supported.yml | 63 -- .github/workflows/unsupported.yml | 71 -- .github/workflows/windows-jruby.yml | 56 -- .github/workflows/windows.yml | 59 -- .jrubyrc | 1 - .overcommit.yml | 31 - .rspec | 4 - .rubocop.yml | 103 -- .rubocop_rspec.yml | 26 - .rubocop_todo.yml | 39 - .simplecov | 32 - Dangerfile | 15 - Gemfile | 56 -- Rakefile | 43 - bin/bundle | 116 --- bin/console | 15 - bin/rake | 27 - bin/rspec | 27 - bin/rubocop | 27 - bin/setup | 8 - docs/images/logo/README.txt | 15 - docs/images/logo/oauth2-logo-124px.png | Bin 13391 -> 0 bytes docs/images/logo/ruby-logo-124px.jpeg | Bin 3935 -> 0 bytes docs/images/logo/ruby-logo-198px.svg | 948 ------------------ gemfiles/README.md | 106 -- gemfiles/f0.gemfile | 11 - gemfiles/f1.gemfile | 9 - gemfiles/f2.gemfile | 9 - gemfiles/jruby_9.1.gemfile | 5 - gemfiles/jruby_9.2.gemfile | 5 - gemfiles/jruby_head.gemfile | 5 - gemfiles/ruby_head.gemfile | 5 - gemfiles/truffleruby.gemfile | 5 - lib/oauth2.rb | 32 - lib/oauth2/access_token.rb | 212 ---- lib/oauth2/authenticator.rb | 75 -- lib/oauth2/client.rb | 354 ------- lib/oauth2/error.rb | 59 -- lib/oauth2/response.rb | 148 --- lib/oauth2/snaky_hash.rb | 8 - lib/oauth2/strategy/assertion.rb | 102 -- lib/oauth2/strategy/auth_code.rb | 47 - lib/oauth2/strategy/base.rb | 11 - lib/oauth2/strategy/client_credentials.rb | 26 - lib/oauth2/strategy/implicit.rb | 38 - lib/oauth2/strategy/password.rb | 29 - lib/oauth2/version.rb | 7 - oauth2.gemspec | 68 -- spec/config/faraday.rb | 3 - spec/config/multi_xml.rb | 3 - spec/config/rspec/rspec_core.rb | 13 - spec/config/rspec/silent_stream.rb | 5 - spec/examples/google_spec.rb | 141 --- spec/ext/backports.rb | 3 - spec/fixtures/README.md | 11 - spec/fixtures/RS256/jwtRS256.key | 51 - spec/fixtures/RS256/jwtRS256.key.pub | 14 - spec/fixtures/google_service_account_key.p12 | Bin 2994 -> 0 bytes spec/oauth2/access_token_spec.rb | 716 ------------- spec/oauth2/authenticator_spec.rb | 126 --- spec/oauth2/client_spec.rb | 948 ------------------ spec/oauth2/error_spec.rb | 645 ------------ spec/oauth2/response_spec.rb | 345 ------- spec/oauth2/snaky_hash_spec.rb | 163 --- spec/oauth2/strategy/assertion_spec.rb | 265 ----- spec/oauth2/strategy/auth_code_spec.rb | 154 --- spec/oauth2/strategy/base_spec.rb | 7 - .../strategy/client_credentials_spec.rb | 97 -- spec/oauth2/strategy/implicit_spec.rb | 40 - spec/oauth2/strategy/password_spec.rb | 59 -- spec/oauth2/version_spec.rb | 39 - spec/spec_helper.rb | 71 -- 82 files changed, 7631 deletions(-) delete mode 100644 .github/FUNDING.yml delete mode 100644 .github/dependabot.yml delete mode 100644 .github/workflows/codeql-analysis.yml delete mode 100644 .github/workflows/coverage.yml delete mode 100644 .github/workflows/danger.yml delete mode 100644 .github/workflows/heads.yml delete mode 100644 .github/workflows/jruby-head.yml delete mode 100644 .github/workflows/macos-ancient.yml delete mode 100644 .github/workflows/macos.yml delete mode 100644 .github/workflows/style.yml delete mode 100644 .github/workflows/supported.yml delete mode 100644 .github/workflows/unsupported.yml delete mode 100644 .github/workflows/windows-jruby.yml delete mode 100644 .github/workflows/windows.yml delete mode 100644 .jrubyrc delete mode 100644 .overcommit.yml delete mode 100644 .rspec delete mode 100644 .rubocop.yml delete mode 100644 .rubocop_rspec.yml delete mode 100644 .rubocop_todo.yml delete mode 100644 .simplecov delete mode 100644 Dangerfile delete mode 100644 Gemfile delete mode 100644 Rakefile delete mode 100755 bin/bundle delete mode 100755 bin/console delete mode 100755 bin/rake delete mode 100755 bin/rspec delete mode 100755 bin/rubocop delete mode 100755 bin/setup delete mode 100644 docs/images/logo/README.txt delete mode 100644 docs/images/logo/oauth2-logo-124px.png delete mode 100644 docs/images/logo/ruby-logo-124px.jpeg delete mode 100644 docs/images/logo/ruby-logo-198px.svg delete mode 100644 gemfiles/README.md delete mode 100644 gemfiles/f0.gemfile delete mode 100644 gemfiles/f1.gemfile delete mode 100644 gemfiles/f2.gemfile delete mode 100644 gemfiles/jruby_9.1.gemfile delete mode 100644 gemfiles/jruby_9.2.gemfile delete mode 100644 gemfiles/jruby_head.gemfile delete mode 100644 gemfiles/ruby_head.gemfile delete mode 100644 gemfiles/truffleruby.gemfile delete mode 100644 lib/oauth2.rb delete mode 100644 lib/oauth2/access_token.rb delete mode 100644 lib/oauth2/authenticator.rb delete mode 100644 lib/oauth2/client.rb delete mode 100644 lib/oauth2/error.rb delete mode 100644 lib/oauth2/response.rb delete mode 100644 lib/oauth2/snaky_hash.rb delete mode 100644 lib/oauth2/strategy/assertion.rb delete mode 100644 lib/oauth2/strategy/auth_code.rb delete mode 100644 lib/oauth2/strategy/base.rb delete mode 100644 lib/oauth2/strategy/client_credentials.rb delete mode 100644 lib/oauth2/strategy/implicit.rb delete mode 100644 lib/oauth2/strategy/password.rb delete mode 100644 lib/oauth2/version.rb delete mode 100644 oauth2.gemspec delete mode 100644 spec/config/faraday.rb delete mode 100644 spec/config/multi_xml.rb delete mode 100644 spec/config/rspec/rspec_core.rb delete mode 100644 spec/config/rspec/silent_stream.rb delete mode 100644 spec/examples/google_spec.rb delete mode 100644 spec/ext/backports.rb delete mode 100644 spec/fixtures/README.md delete mode 100644 spec/fixtures/RS256/jwtRS256.key delete mode 100644 spec/fixtures/RS256/jwtRS256.key.pub delete mode 100644 spec/fixtures/google_service_account_key.p12 delete mode 100644 spec/oauth2/access_token_spec.rb delete mode 100644 spec/oauth2/authenticator_spec.rb delete mode 100644 spec/oauth2/client_spec.rb delete mode 100644 spec/oauth2/error_spec.rb delete mode 100644 spec/oauth2/response_spec.rb delete mode 100644 spec/oauth2/snaky_hash_spec.rb delete mode 100644 spec/oauth2/strategy/assertion_spec.rb delete mode 100644 spec/oauth2/strategy/auth_code_spec.rb delete mode 100644 spec/oauth2/strategy/base_spec.rb delete mode 100644 spec/oauth2/strategy/client_credentials_spec.rb delete mode 100644 spec/oauth2/strategy/implicit_spec.rb delete mode 100644 spec/oauth2/strategy/password_spec.rb delete mode 100644 spec/oauth2/version_spec.rb delete mode 100644 spec/spec_helper.rb diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index e21342e5..00000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,13 +0,0 @@ -# These are supported funding model platforms - -github: [pboling] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: galtzo # Replace with a single Patreon username -open_collective: # Replace with a single Open Collective username -ko_fi: pboling # Replace with a single Ko-fi username -tidelift: rubygems/oauth2 # 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: pboling # Replace with a single Liberapay username -issuehunt: pboling # Replace with a single IssueHunt username -otechie: # Replace with a single Otechie username -lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry -custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 89c4a1c3..00000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,10 +0,0 @@ -version: 2 -updates: - - package-ecosystem: bundler - directory: "/" - schedule: - interval: daily - time: "04:28" - open-pull-requests-limit: 10 - ignore: - - dependency-name: "rubocop-lts" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index ca362f89..00000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,70 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: "CodeQL" - -on: - push: - branches: [ master, main, "*-stable" ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ master, main, "*-stable" ] - schedule: - - cron: '35 1 * * 5' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: [ 'ruby' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] - # Learn more about CodeQL language support at https://git.io/codeql-language-support - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 - - # i️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml deleted file mode 100644 index 2ae6fa6e..00000000 --- a/.github/workflows/coverage.yml +++ /dev/null @@ -1,115 +0,0 @@ -name: Code Coverage - -env: - CI_CODECOV: true - COVER_ALL: true - -on: - push: - branches: - - 'main' - - 'master' - - '*-maintenance' - - '*-dev' - - '*-stable' - tags: - - '!*' # Do not execute on tags - pull_request: - branches: - - '*' - # Allow manually triggering the workflow. - workflow_dispatch: - -# Cancels all previous workflow runs for the same branch that have not yet completed. -concurrency: - # The concurrency group contains the workflow name and the branch name. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - test: - name: Specs with Coverage - Ruby ${{ matrix.ruby }} ${{ matrix.name_extra || '' }} - if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" - strategy: - fail-fast: false - matrix: - experimental: [false] - rubygems: - - latest - bundler: - - latest - ruby: - - "2.7" - - runs-on: ubuntu-latest - continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} - steps: - - uses: amancevice/setup-code-climate@v0 - name: CodeClimate Install - if: matrix.ruby == '2.7' && github.event_name != 'pull_request' && always() - with: - cc_test_reporter_id: ${{ secrets.CC_TEST_REPORTER_ID }} - - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup Ruby & Bundle - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - rubygems: ${{ matrix.rubygems }} - bundler: ${{ matrix.bundler }} - bundler-cache: true - - - name: CodeClimate Pre-build Notification - run: cc-test-reporter before-build - if: matrix.ruby == '2.7' && github.event_name != 'pull_request' && always() - continue-on-error: ${{ matrix.experimental != 'false' }} - - - name: Run tests - run: bundle exec rake test - - - name: CodeClimate Post-build Notification - run: cc-test-reporter after-build - if: matrix.ruby == '2.7' && github.event_name != 'pull_request' && always() - continue-on-error: ${{ matrix.experimental != 'false' }} - - - name: Code Coverage Summary Report - uses: irongut/CodeCoverageSummary@v1.2.0 - with: - filename: ./coverage/coverage.xml - badge: true - fail_below_min: true - format: markdown - hide_branch_rate: true - hide_complexity: true - indicators: true - output: both - thresholds: '100 95' - continue-on-error: ${{ matrix.experimental != 'false' }} - - - name: Add Coverage PR Comment - uses: marocchino/sticky-pull-request-comment@v2 - if: matrix.ruby == '2.7' && always() - with: - recreate: true - path: code-coverage-results.md - continue-on-error: ${{ matrix.experimental != 'false' }} - - - name: Coveralls - uses: coverallsapp/github-action@master - if: matrix.ruby == '2.7' && github.event_name != 'pull_request' && always() - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - continue-on-error: ${{ matrix.experimental != 'false' }} - -# Using the codecov gem instead. -# - name: CodeCov -# uses: codecov/codecov-action@v2 -# if: matrix.ruby == '2.7' && github.event_name != 'pull_request' && always() -# with: -# files: ./coverage/coverage.xml -# flags: unittests -# name: codecov-upload -# fail_ci_if_error: true -# continue-on-error: ${{ matrix.experimental != 'false' }} diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml deleted file mode 100644 index c835a410..00000000 --- a/.github/workflows/danger.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: What's up Danger? - -on: - pull_request: - branches: - - 'main' - - 'master' - - '*-stable' - -jobs: - danger: - runs-on: ubuntu-latest - env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps - BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile - if: false - # if: github.event_name == 'pull_request' # if only run pull request when multiple trigger workflow - strategy: - fail-fast: false - matrix: - gemfile: - - f2 - rubygems: - - latest - bundler: - - latest - ruby: - - "2.7" - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Setup Ruby & Bundle - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - rubygems: ${{ matrix.rubygems }} - bundler: ${{ matrix.bundler }} - bundler-cache: true - - uses: MeilCli/danger-action@v5 - with: - plugins_file: 'Gemfile' - install_path: 'vendor/bundle' - danger_file: 'Dangerfile' - danger_id: 'danger-pr' - env: - DANGER_GITHUB_API_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN }} diff --git a/.github/workflows/heads.yml b/.github/workflows/heads.yml deleted file mode 100644 index 40587841..00000000 --- a/.github/workflows/heads.yml +++ /dev/null @@ -1,70 +0,0 @@ -name: Heads - -on: - push: - branches: - - 'main' - - 'master' - - '*-maintenance' - - '*-dev' - - '*-stable' - tags: - - '!*' # Do not execute on tags - pull_request: - branches: - - '*' - # Allow manually triggering the workflow. - workflow_dispatch: - -# Cancels all previous workflow runs for the same branch that have not yet completed. -concurrency: - # The concurrency group contains the workflow name and the branch name. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - test: - name: Specs - Ruby ${{ matrix.ruby }} ${{matrix.gemfile}} ${{ matrix.name_extra || '' }} - env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps - BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile - if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" - strategy: - fail-fast: false - matrix: - experimental: [true] - gemfile: - - f0 - - f1 - - f2 - rubygems: - - latest - bundler: - - latest - ruby: - - truffleruby+graalvm-head - - truffleruby-head - - ruby-head - include: - # Includes a new variable experimental with a value of false - # for the matrix legs matching rubygems: latest, which is all of them. - # This is here for parity with the unsupported.yml - # This is a hack. Combined with continue-on-error it should allow us - # to have a workflow with allowed failure. - # This is the "supported" build matrix, so only the "head" builds are experimental here. - - rubygems: latest - experimental: true - - runs-on: ubuntu-latest - continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Setup Ruby & Bundle - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - rubygems: ${{ matrix.rubygems }} - bundler: ${{ matrix.bundler }} - bundler-cache: true - - name: Run tests - run: bundle exec rake test diff --git a/.github/workflows/jruby-head.yml b/.github/workflows/jruby-head.yml deleted file mode 100644 index 6e56c895..00000000 --- a/.github/workflows/jruby-head.yml +++ /dev/null @@ -1,69 +0,0 @@ -name: JRuby Head - -on: - push: - branches: - - 'main' - - 'master' - - '*-maintenance' - - '*-dev' - - '*-stable' - tags: - - '!*' # Do not execute on tags - pull_request: - branches: - - '*' - # Allow manually triggering the workflow. - workflow_dispatch: - -# Cancels all previous workflow runs for the same branch that have not yet completed. -concurrency: - # The concurrency group contains the workflow name and the branch name. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - test: - name: Specs - Ruby ${{ matrix.ruby }} ${{matrix.gemfile}} ${{ matrix.name_extra || '' }} - env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps - BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile - if: "false" - # if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" - strategy: - fail-fast: false - matrix: - experimental: [true] - gemfile: - - f0 - - f1 - - f2 - rubygems: - - latest - bundler: - - latest - ruby: - - jruby-head - include: - # Includes a new variable experimental with a value of false - # for the matrix legs matching rubygems: latest, which is all of them. - # This is here for parity with the unsupported.yml - # This is a hack. Combined with continue-on-error it should allow us - # to have a workflow with allowed failure. - # This is the "supported" build matrix, so only the "head" builds are experimental here. - - rubygems: latest - experimental: true - - runs-on: ubuntu-latest - continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Setup Ruby & Bundle - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - rubygems: ${{ matrix.rubygems }} - bundler: ${{ matrix.bundler }} - bundler-cache: true - - name: Run tests - run: bundle exec rake test diff --git a/.github/workflows/macos-ancient.yml b/.github/workflows/macos-ancient.yml deleted file mode 100644 index 847644cd..00000000 --- a/.github/workflows/macos-ancient.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: Old MacOS - -on: - push: - branches: - - 'main' - - 'master' - - '*-maintenance' - - '*-dev' - - '*-stable' - tags: - - '!*' # Do not execute on tags - pull_request: - branches: - - '*' - # Allow manually triggering the workflow. - workflow_dispatch: - -# Cancels all previous workflow runs for the same branch that have not yet completed. -concurrency: - # The concurrency group contains the workflow name and the branch name. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - test: - name: Specs - Ruby ${{ matrix.ruby }} ${{matrix.gemfile}} ${{ matrix.name_extra || '' }} - env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps - BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile - if: "false" - # if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" - strategy: - fail-fast: false - matrix: - experimental: [true] - gemfile: - - f0 - rubygems: - - "2.7.11" - ruby: - - "1.9" - - "2.0" - - "2.1" - - "2.2" - - runs-on: macos-10.15 - continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Setup Ruby & Bundle - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - rubygems: ${{ matrix.rubygems }} - bundler-cache: true - - name: Run tests - run: bundle exec rake test diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml deleted file mode 100644 index 447aa830..00000000 --- a/.github/workflows/macos.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: MacOS - -on: - push: - branches: - - 'main' - - 'master' - - '*-maintenance' - - '*-dev' - - '*-stable' - tags: - - '!*' # Do not execute on tags - pull_request: - branches: - - '*' - # Allow manually triggering the workflow. - workflow_dispatch: - -# Cancels all previous workflow runs for the same branch that have not yet completed. -concurrency: - # The concurrency group contains the workflow name and the branch name. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - test: - name: Specs - Ruby ${{ matrix.ruby }} ${{matrix.gemfile}} ${{ matrix.name_extra || '' }} - env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps - BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile - if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" - strategy: - fail-fast: false - matrix: - experimental: [true] - gemfile: - - f2 - rubygems: - - latest - bundler: - - latest - ruby: - - "2.7" - - "3.0" - - "3.1" - - truffleruby - - jruby - - runs-on: macos-latest - continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Setup Ruby & Bundle - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - rubygems: ${{ matrix.rubygems }} - bundler: ${{ matrix.bundler }} - bundler-cache: true - - name: Run tests - run: bundle exec rake test diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml deleted file mode 100644 index fdf9aaa3..00000000 --- a/.github/workflows/style.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Code Style Checks - -on: - push: - branches: - - 'main' - - 'master' - - '*-maintenance' - - '*-dev' - - '*-stable' - tags: - - '!*' # Do not execute on tags - pull_request: - branches: - - '*' - -jobs: - rubocop: - name: Rubocop - if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" - strategy: - fail-fast: false - matrix: - experimental: [false] - rubygems: - - latest - bundler: - - latest - ruby: - - "2.7" - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Setup Ruby & Bundle - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - rubygems: ${{ matrix.rubygems }} - bundler: ${{ matrix.bundler }} - bundler-cache: true - - name: Run Rubocop - run: bundle exec rubocop -DESP diff --git a/.github/workflows/supported.yml b/.github/workflows/supported.yml deleted file mode 100644 index 069f8d54..00000000 --- a/.github/workflows/supported.yml +++ /dev/null @@ -1,63 +0,0 @@ -name: Official Support - -on: - push: - branches: - - 'main' - - 'master' - - '*-maintenance' - - '*-dev' - - '*-stable' - tags: - - '!*' # Do not execute on tags - pull_request: - branches: - - '*' - # Allow manually triggering the workflow. - workflow_dispatch: - -# Cancels all previous workflow runs for the same branch that have not yet completed. -concurrency: - # The concurrency group contains the workflow name and the branch name. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - test: - name: Specs - Ruby ${{ matrix.ruby }} ${{matrix.gemfile}} ${{ matrix.name_extra || '' }} - env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps - BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile - if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" - strategy: - fail-fast: false - matrix: - experimental: [false] - gemfile: - - f0 - - f1 - - f2 - rubygems: - - latest - bundler: - - latest - ruby: - - "2.7" - - "3.0" - - "3.1" - - truffleruby - - jruby - - runs-on: ubuntu-latest - continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Setup Ruby & Bundle - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - rubygems: ${{ matrix.rubygems }} - bundler: ${{ matrix.bundler }} - bundler-cache: true - - name: Run tests - run: bundle exec rake test diff --git a/.github/workflows/unsupported.yml b/.github/workflows/unsupported.yml deleted file mode 100644 index 40f3949c..00000000 --- a/.github/workflows/unsupported.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: Unofficial Support - -on: - push: - branches: - - 'main' - - 'master' - - '*-maintenance' - - '*-dev' - - '*-stable' - tags: - - '!*' # Do not execute on tags - pull_request: - branches: - - '*' - # Allow manually triggering the workflow. - workflow_dispatch: - -# Cancels all previous workflow runs for the same branch that have not yet completed. -concurrency: - # The concurrency group contains the workflow name and the branch name. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - test: - name: Specs - Ruby ${{ matrix.ruby }} ${{matrix.gemfile}} ${{ matrix.name_extra || '' }} - env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps - BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile - if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" - strategy: - fail-fast: false - matrix: - experimental: [false] - gemfile: - - f0 - - f1 - - f2 - rubygems: - - latest - bundler: - - latest - ruby: - - "2.3" - - "2.4" - - "2.5" - - "2.6" - exclude: - - ruby: "2.3" - gemfile: "f1" - - ruby: "2.3" - gemfile: "f2" - - ruby: "2.4" - gemfile: "f2" - - ruby: "2.5" - gemfile: "f2" - - runs-on: ubuntu-20.04 - continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Setup Ruby & Bundle - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - rubygems: ${{ matrix.rubygems }} - bundler: ${{ matrix.bundler }} - bundler-cache: true - - name: Run tests - run: bundle exec rake test diff --git a/.github/workflows/windows-jruby.yml b/.github/workflows/windows-jruby.yml deleted file mode 100644 index f7e57780..00000000 --- a/.github/workflows/windows-jruby.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: Windows JRuby - -on: - push: - branches: - - 'main' - - 'master' - - '*-maintenance' - - '*-dev' - - '*-stable' - tags: - - '!*' # Do not execute on tags - pull_request: - branches: - - '*' - # Allow manually triggering the workflow. - workflow_dispatch: - -# Cancels all previous workflow runs for the same branch that have not yet completed. -concurrency: - # The concurrency group contains the workflow name and the branch name. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - test: - name: Specs - Ruby ${{ matrix.ruby }} ${{matrix.gemfile}} ${{ matrix.name_extra || '' }} - env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps - BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile - if: "false" - # if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" - strategy: - fail-fast: false - matrix: - experimental: [true] - gemfile: - - f2 - bundler: - - none - ruby: - - jruby - - runs-on: windows-latest - continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Setup Ruby & Bundle - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - rubygems: ${{ matrix.rubygems }} - bundler: ${{ matrix.bundler }} - bundler-cache: true - - name: Run tests - run: bundle exec rake test diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml deleted file mode 100644 index 19df6468..00000000 --- a/.github/workflows/windows.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Windows - -on: - push: - branches: - - 'main' - - 'master' - - '*-maintenance' - - '*-dev' - - '*-stable' - tags: - - '!*' # Do not execute on tags - pull_request: - branches: - - '*' - # Allow manually triggering the workflow. - workflow_dispatch: - -# Cancels all previous workflow runs for the same branch that have not yet completed. -concurrency: - # The concurrency group contains the workflow name and the branch name. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - test: - name: Specs - Ruby ${{ matrix.ruby }} ${{matrix.gemfile}} ${{ matrix.name_extra || '' }} - env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps - BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile - if: "!contains(github.event.commits[0].message, '[ci skip]') && !contains(github.event.commits[0].message, '[skip ci]')" - strategy: - fail-fast: false - matrix: - experimental: [false] - gemfile: - - f2 - rubygems: - - latest - bundler: - - latest - ruby: - - "2.7" - - "3.0" - - "3.1" - - runs-on: windows-latest - continue-on-error: ${{ matrix.experimental || endsWith(matrix.ruby, 'head') }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Setup Ruby & Bundle - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - rubygems: ${{ matrix.rubygems }} - bundler: ${{ matrix.bundler }} - bundler-cache: true - - name: Run tests - run: bundle exec rake test diff --git a/.jrubyrc b/.jrubyrc deleted file mode 100644 index ec033eeb..00000000 --- a/.jrubyrc +++ /dev/null @@ -1 +0,0 @@ -debug.fullTrace=true diff --git a/.overcommit.yml b/.overcommit.yml deleted file mode 100644 index 9fbb7f23..00000000 --- a/.overcommit.yml +++ /dev/null @@ -1,31 +0,0 @@ -# Use this file to configure the Overcommit hooks you wish to use. This will -# extend the default configuration defined in: -# https://github.com/sds/overcommit/blob/master/config/default.yml -# -# At the topmost level of this YAML file is a key representing type of hook -# being run (e.g. pre-commit, commit-msg, etc.). Within each type you can -# customize each hook, such as whether to only run it on certain files (via -# `include`), whether to only display output if it fails (via `quiet`), etc. -# -# For a complete list of hooks, see: -# https://github.com/sds/overcommit/tree/master/lib/overcommit/hook -# -# For a complete list of options that you can use to customize hooks, see: -# https://github.com/sds/overcommit#configuration -# -# Uncomment the following lines to make the configuration take effect. - -PreCommit: - RuboCop: - enabled: true - on_warn: fail # Treat all warnings as failures - - TrailingWhitespace: - enabled: true - -PostCheckout: - ALL: # Special hook name that customizes all hooks of this type - quiet: true # Change all post-checkout hooks to only display output on failure -# -# IndexTags: -# enabled: true # Generate a tags file with `ctags` each time HEAD changes diff --git a/.rspec b/.rspec deleted file mode 100644 index 2db90875..00000000 --- a/.rspec +++ /dev/null @@ -1,4 +0,0 @@ ---format documentation ---require spec_helper ---color ---order random diff --git a/.rubocop.yml b/.rubocop.yml deleted file mode 100644 index 6c9e1f99..00000000 --- a/.rubocop.yml +++ /dev/null @@ -1,103 +0,0 @@ -inherit_from: - - .rubocop_todo.yml - - .rubocop_rspec.yml - -inherit_gem: - rubocop-lts: rubocop-lts.yml - -require: - - 'rubocop-md' - # Can be added once we reach rubocop-ruby2_3 - # - 'rubocop-packaging' - - 'rubocop-performance' - - 'rubocop-rake' - - 'rubocop-rspec' - -AllCops: - DisplayCopNames: true # Display the name of the failing cops - Exclude: - - 'gemfiles/vendor/**/*' - - 'vendor/**/*' - - '**/.irbrc' - -Metrics/BlockLength: - ExcludedMethods: - - context - - describe - - it - - shared_context - - shared_examples - - shared_examples_for - - namespace - - draw - -Gemspec/RequiredRubyVersion: - Enabled: false - -Metrics/BlockNesting: - Max: 2 - -Metrics/LineLength: - Enabled: false - -Metrics/ParameterLists: - Max: 4 - -Layout/AccessModifierIndentation: - EnforcedStyle: outdent - -Layout/DotPosition: - EnforcedStyle: trailing - -Layout/SpaceInsideHashLiteralBraces: - EnforcedStyle: no_space - -Lint/UnusedBlockArgument: - Exclude: - - 'spec/**/*.rb' - - 'gemfiles/vendor/**/*' - - 'vendor/**/*' - - '**/.irbrc' - -RSpec/DescribeClass: - Exclude: - - 'spec/examples/*' - -RSpec/NestedGroups: - Enabled: false - -Style/ClassVars: - Enabled: false - -Style/CollectionMethods: - PreferredMethods: - map: 'collect' - reduce: 'inject' - find: 'detect' - find_all: 'select' - -Style/Documentation: - Enabled: false - -Style/DoubleNegation: - Enabled: false - -Style/EmptyMethod: - EnforcedStyle: expanded - -Style/Encoding: - Enabled: false - -# Does not work with older rubies -#Style/MapToHash: -# Enabled: false - -# Does not work with older rubies -#Style/RedundantBegin: -# Enabled: false - -Style/TrailingCommaInArrayLiteral: - EnforcedStyleForMultiline: comma - -Style/TrailingCommaInHashLiteral: - EnforcedStyleForMultiline: comma diff --git a/.rubocop_rspec.yml b/.rubocop_rspec.yml deleted file mode 100644 index 347777dc..00000000 --- a/.rubocop_rspec.yml +++ /dev/null @@ -1,26 +0,0 @@ -RSpec/FilePath: - Enabled: false - -RSpec/MultipleExpectations: - Enabled: false - -RSpec/NamedSubject: - Enabled: false - -RSpec/ExampleLength: - Enabled: false - -RSpec/VerifiedDoubles: - Enabled: false - -RSpec/MessageSpies: - Enabled: false - -RSpec/InstanceVariable: - Enabled: false - -RSpec/NestedGroups: - Enabled: false - -RSpec/ExpectInHook: - Enabled: false diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml deleted file mode 100644 index b3a3b14d..00000000 --- a/.rubocop_todo.yml +++ /dev/null @@ -1,39 +0,0 @@ -# This configuration was generated by -# `rubocop --auto-gen-config` -# on 2022年07月13日 09:52:51 +0700 using RuboCop version 0.68.1. -# The point is for the user to remove these configuration records -# one by one as the offenses are removed from the code base. -# Note that changes in the inspected code, or installation of new -# versions of RuboCop, may require this file to be generated again. - -# Offense count: 9 -Metrics/AbcSize: - Max: 35 - -# Offense count: 6 -# Configuration parameters: CountComments, ExcludedMethods. -# ExcludedMethods: refine -Metrics/BlockLength: - Max: 35 - -# Offense count: 5 -Metrics/CyclomaticComplexity: - Max: 12 - -# Offense count: 10 -# Configuration parameters: CountComments, ExcludedMethods. -Metrics/MethodLength: - Max: 34 - -# Offense count: 3 -Metrics/PerceivedComplexity: - Max: 13 - -# Offense count: 10 -# Configuration parameters: Prefixes. -# Prefixes: when, with, without -RSpec/ContextWording: - Exclude: - - 'spec/oauth2/access_token_spec.rb' - - 'spec/oauth2/authenticator_spec.rb' - - 'spec/oauth2/client_spec.rb' diff --git a/.simplecov b/.simplecov deleted file mode 100644 index 423aa578..00000000 --- a/.simplecov +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -# To get coverage -# On Local, default (HTML) output, it just works, coverage is turned on: -# bundle exec rspec spec -# On Local, all output formats: -# COVER_ALL=true bundle exec rspec spec -# -# On CI, all output formats, the ENV variables CI is always set, -# and COVER_ALL, and CI_CODECOV, are set in the coverage.yml workflow only, -# so coverage only runs in that workflow, and outputs all formats. -# - -if RUN_COVERAGE - SimpleCov.start do - enable_coverage :branch - primary_coverage :branch - add_filter 'spec' - add_filter 'lib/oauth2/version.rb' - track_files '**/*.rb' - - if ALL_FORMATTERS - command_name "#{ENV['GITHUB_WORKFLOW']} Job #{ENV['GITHUB_RUN_ID']}:#{ENV['GITHUB_RUN_NUMBER']}" - else - formatter SimpleCov::Formatter::HTMLFormatter - end - - minimum_coverage(line: 100, branch: 100) - end -else - puts "Not running coverage on #{RUBY_ENGINE} #{RUBY_VERSION}" -end diff --git a/Dangerfile b/Dangerfile deleted file mode 100644 index 518ea63a..00000000 --- a/Dangerfile +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -# Ideas... -# 1. Check for hashtags in PR title, and disallow changes to changelog? -# e.g. github.pr_title.include? "#trivial" - -# Make it more obvious that a PR is a work in progress and shouldn't be merged yet -warn('PR is classed as Work in Progress') if github.pr_title.include? '[WIP]' - -# Warn when there is a big PR -warn('Big PR') if git.lines_of_code> 500 - -# Don't let testing shortcuts get into master by accident -raise('fdescribe left in tests') if `grep -r fdescribe specs/ `.length> 1 -raise('fit left in tests') if `grep -r fit specs/ `.length> 1 diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 5f04e5e4..00000000 --- a/Gemfile +++ /dev/null @@ -1,56 +0,0 @@ -# frozen_string_literal: true - -source 'https://rubygems.org' - -gemspec - -git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } - -gem 'rake', '~> 13.0' - -gem 'rspec', '~> 3.0' - -ruby_version = Gem::Version.new(RUBY_VERSION) -minimum_version = ->(version, engine = 'ruby') { ruby_version>= Gem::Version.new(version) && RUBY_ENGINE == engine } -linting = minimum_version.call('2.7') -coverage = minimum_version.call('2.7') -debug = minimum_version.call('2.5') - -gem 'overcommit', '~> 0.58' if linting - -platforms :mri do - if linting - # Danger is incompatible with Faraday 2 (for now) - # see: https://github.com/danger/danger/issues/1349 - # gem 'danger', '~> 8.4' - gem 'rubocop-md', require: false - # Can be added once we reach rubocop-lts>= v10 (i.e. drop Ruby 2.2) - # gem 'rubocop-packaging', require: false - gem 'rubocop-performance', require: false - gem 'rubocop-rake', require: false - gem 'rubocop-rspec', require: false - gem 'rubocop-thread_safety', require: false - end - if coverage - gem 'codecov', '~> 0.6' # For CodeCov - gem 'simplecov', '~> 0.21', require: false - gem 'simplecov-cobertura' # XML for Jenkins - gem 'simplecov-json' # For CodeClimate - gem 'simplecov-lcov', '~> 0.8', require: false - end - if debug - # Add `byebug` to your code where you want to drop to REPL - gem 'byebug' - end -end -platforms :jruby do - # Add `binding.pry` to your code where you want to drop to REPL - gem 'pry-debugger-jruby' -end - -### deps for documentation and rdoc.info -group :documentation do - gem 'github-markup', platform: :mri - gem 'redcarpet', platform: :mri - gem 'yard', require: false -end diff --git a/Rakefile b/Rakefile deleted file mode 100644 index eb413795..00000000 --- a/Rakefile +++ /dev/null @@ -1,43 +0,0 @@ -# encoding: utf-8 -# frozen_string_literal: true - -# !/usr/bin/env rake - -require 'bundler/gem_tasks' - -begin - require 'rspec/core/rake_task' - RSpec::Core::RakeTask.new(:spec) -rescue LoadError - desc 'spec task stub' - task :spec do - warn 'rspec is disabled' - end -end -desc 'alias test task to spec' -task test: :spec - -begin - require 'rubocop/rake_task' - RuboCop::RakeTask.new do |task| - task.options = ['-D'] # Display the name of the failing cops - end -rescue LoadError - desc 'rubocop task stub' - task :rubocop do - warn 'RuboCop is disabled' - end -end - -# namespace :doc do -# require 'rdoc/task' -# require 'oauth2/version' -# RDoc::Task.new do |rdoc| -# rdoc.rdoc_dir = 'rdoc' -# rdoc.title = "oauth2 #{OAuth2::Version}" -# rdoc.main = 'README.md' -# rdoc.rdoc_files.include('README.md', 'LICENSE.md', 'lib/**/*.rb') -# end -# end - -task default: %i[test rubocop] diff --git a/bin/bundle b/bin/bundle deleted file mode 100755 index fece50fe..00000000 --- a/bin/bundle +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -# -# This file was generated by Bundler. -# -# The application 'bundle' is installed as part of a gem, and -# this file is here to facilitate running it. -# - -require 'rubygems' - -m = Module.new do -module_function - - def invoked_as_script? - File.expand_path($PROGRAM_NAME) == File.expand_path(__FILE__) - end - - def env_var_version - ENV['BUNDLER_VERSION'] - end - - def cli_arg_version - return unless invoked_as_script? # don't want to hijack other binstubs - return unless 'update'.start_with?(ARGV.first || ' ') # must be running `bundle update` - - bundler_version = nil - update_index = nil - ARGV.each_with_index do |a, i| - bundler_version = a if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN - next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/ - - bundler_version = Regexp.last_match(1) - update_index = i - end - bundler_version - end - - def gemfile - gemfile = ENV['BUNDLE_GEMFILE'] - return gemfile if gemfile && !gemfile.empty? - - File.expand_path('../Gemfile', __dir__) - end - - def lockfile - lockfile = - case File.basename(gemfile) - when 'gems.rb' then gemfile.sub(/\.rb$/, gemfile) - else "#{gemfile}.lock" - end - File.expand_path(lockfile) - end - - def lockfile_version - return unless File.file?(lockfile) - - lockfile_contents = File.read(lockfile) - return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/ - - Regexp.last_match(1) - end - - def bundler_requirement - @bundler_requirement ||= - env_var_version || cli_arg_version || - bundler_requirement_for(lockfile_version) - end - - def bundler_requirement_for(version) - return "#{Gem::Requirement.default}.a" unless version - - bundler_gem_version = Gem::Version.new(version) - - requirement = bundler_gem_version.approximate_recommendation - - return requirement unless Gem.rubygems_version < Gem::Version.new('2.7.0') - - requirement += '.a' if bundler_gem_version.prerelease? - - requirement - end - - def load_bundler! - ENV['BUNDLE_GEMFILE'] ||= gemfile - - activate_bundler - end - - def activate_bundler - gem_error = activation_error_handling do - gem 'bundler', bundler_requirement - end - return if gem_error.nil? - - require_error = activation_error_handling do - require 'bundler/version' - end - return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION)) - - warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`" - exit 42 - end - - def activation_error_handling - yield - nil - rescue StandardError, LoadError => e - e - end -end - -m.load_bundler! - -load Gem.bin_path('bundler', 'bundle') if m.invoked_as_script? diff --git a/bin/console b/bin/console deleted file mode 100755 index b3c40a59..00000000 --- a/bin/console +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -require 'bundler/setup' -require 'oauth2' - -# You can add fixtures and/or initialization code here to make experimenting -# with your gem easier. You can also use a different console, if you like. - -# (If you use this, don't forget to add pry to your Gemfile!) -# require "pry" -# Pry.start - -require 'irb' -IRB.start(__FILE__) diff --git a/bin/rake b/bin/rake deleted file mode 100755 index 5f615c2a..00000000 --- a/bin/rake +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -# -# This file was generated by Bundler. -# -# The application 'rake' is installed as part of a gem, and -# this file is here to facilitate running it. -# - -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) - -bundle_binstub = File.expand_path('bundle', __dir__) - -if File.file?(bundle_binstub) - if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/ - load(bundle_binstub) - else - abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. -Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") - end -end - -require 'rubygems' -require 'bundler/setup' - -load Gem.bin_path('rake', 'rake') diff --git a/bin/rspec b/bin/rspec deleted file mode 100755 index d3f4959a..00000000 --- a/bin/rspec +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -# -# This file was generated by Bundler. -# -# The application 'rspec' is installed as part of a gem, and -# this file is here to facilitate running it. -# - -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) - -bundle_binstub = File.expand_path('bundle', __dir__) - -if File.file?(bundle_binstub) - if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/ - load(bundle_binstub) - else - abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. -Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") - end -end - -require 'rubygems' -require 'bundler/setup' - -load Gem.bin_path('rspec-core', 'rspec') diff --git a/bin/rubocop b/bin/rubocop deleted file mode 100755 index cc105e8d..00000000 --- a/bin/rubocop +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -# -# This file was generated by Bundler. -# -# The application 'rubocop' is installed as part of a gem, and -# this file is here to facilitate running it. -# - -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) - -bundle_binstub = File.expand_path('bundle', __dir__) - -if File.file?(bundle_binstub) - if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/ - load(bundle_binstub) - else - abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. -Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") - end -end - -require 'rubygems' -require 'bundler/setup' - -load Gem.bin_path('rubocop', 'rubocop') diff --git a/bin/setup b/bin/setup deleted file mode 100755 index dce67d86..00000000 --- a/bin/setup +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -IFS=$'\n\t' -set -vx - -bundle install - -# Do any other automated setup that you need to do here diff --git a/docs/images/logo/README.txt b/docs/images/logo/README.txt deleted file mode 100644 index bb405554..00000000 --- a/docs/images/logo/README.txt +++ /dev/null @@ -1,15 +0,0 @@ -The OAuth 2.0 Logo - oauth2-logo-124px.png (resized) - -https://oauth.net/about/credits/ - -The OAuth logo was designed by Chris Messina. - ---- - -The Ruby Logo - ruby-logo-124px.jpeg (resized) - -https://www.ruby-lang.org/en/about/logo/ - -Yukihiro Matsumoto, Ruby Visual Identity Team, CC BY-SA 2.5 - -https://creativecommons.org/licenses/by-sa/2.5 diff --git a/docs/images/logo/oauth2-logo-124px.png b/docs/images/logo/oauth2-logo-124px.png deleted file mode 100644 index 41a8d35aa834108cdfb86d5b9d8eef369367c324..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13391 zcmV-VG_cEwP);*gahA3bM0|@pO zijgSR5H$hG`#ry%`Olp1&F;Osmk@&7GxN;uzWqG^r=N4)9n81+Hs9vkRi`anxNwta zKJ%H|9B{w^dp-HdPu~BDPkiFS3ドルFJ43ドルNl`k666le*0bc;0Hf=<^aq=zt#7-&ww*Brk+Uxk;ci(;CuDkBK{~hjdhrMon>sxQL_uhMNGMI1cS3BZ}BQ|@} zo8GkhYhU}?1uuX3%a4B1i(Yi{^Pm6xCC`5LvsWB+&_P`R-z`|MpnJj-p3pt|(U0yP z{_ux)1$cM=``^DSz`K3+*{8ev-S6HN=v@Ka?Xkxm-5u|E8ドルN_RcU-ak_S-MH?QL&+ z@-1(9%cF00vzslr@r`f1d);!g!F*eH?USGUD>s!0mz3z40%U<@ft^n^2ipmp;s$XYgS)3c z{pmwt*nj{1yL;dJ-b2t!!@F&_-L|{ct!~wAv&}YLK5u%{o36geO>T1D7F%p_!VPbD z!$UUSc;g-Gkv1I6x3%9+Ipvg{zy9^FFZ$vazj)ebKJ%HCpZ@fxyAOZ(!`-{z{qFAV zZ-0CDwzs{lJL;&Tx(|Hd1KkNHoX~yiTi@z_{No>Yzx?GdyYtUKzgxC!S@+k!{|~1`q#g@fB*a6 zq2cfU{O3Pi`+eogl|x#!YE^g1C6{!UUV3Tw-~ax%TfKU9*KY*lH^2E!_oXj=se9F{ zUNsbet+v{#E0DYET<1dnx?l0d4?FkV zb1(Sa?|wJbx&pl`(7PZ0@Q2;mXP@0IS+ZmZ@IU|g&k*hP*%ASu(Jin6f4J?k%PtG~ z9U2}NA?P>zfANc7bnkoL`@{r8ZLi1f3hbc}Y_P!w7u4^=inblhxACpa?hb$Z+ux2Y z;FbfaKp$%EuYdjP?zg}FZTE*i`~k>J^BPp=G0kg$?mB<^)1qxs{w;5lolz3qn4w4t z{IVd-_RuAis~`xrj|81SjBB*tH7e2vJOaAU3k1;Awm z)F1!&NB(yRbm*dsF6#dBm%oe!9R+aa_`LJZ>mK;P2Mz(_Op?upNsg$H@+bopzQz;U0ifz(biXYpf~=}kA8G$ndoIq zH!UtRPP2gw?0g4)fVJIcfS*y*1Gp&sUiZ3J_mGD?q+7grai@LlYhUZ`agTd+`|i7M z_ktI^z_~zPg911cUVd7{F0vQc4WD6DbC0Jz?P<~u(6xxki#er6fnchr_vdjlkamqy zXP$W`(=AeFa%mG_qv6Bf-}%mWhEe%TUhR80d~FLj6Ws#-!WX_U zfG3!1{r*s+4?p~HDcUe#tDJo3yQ^5!En2il9~?gl_|o{t1n|)5Mf+YYfPVk`-@jQ! z*@u@oz4Y7P{`N4+KJB#A_(4FUH2~*>&pGFuVFBeyPkK`Kl&3s}3C2vIj@5J|XcOQC z@P|J1p`!X(0Y?G6jp7MURq%#@Z@u-_-HmQ^BdOXD{HhCd7!fBow_*5w(`c!uA@@@kc5Mf6G8MuM(Y%%S3jm~)ng1!66R_nV;Ux2j$M zPc(nf{{+y79d_8pRrWll0PoN-#~d>>P4Fc7LzYWIp)ykZDq z4UW*7RMJM8c7B|o-TBUU#%LNnH02S12Rz^b00T0x5#ZMVUE{BrZzf&2Rw1WteElSi zrFi2T-`Mx<$cqh>>IQT*pt04P-~8qi0aCyZ1Fj$Z;0Jta2-vfNPJ_4s{u|(OX@cWB zZ6^pKXaM&?k9oJvd4e_?je!5CM?I?h;0HhGxK!dsf({@VgEWr7ドルO8tCErlCkhbC== zKZP4~jcZ)v#QNOoDhGCH!uGFu&1=qPDhv4TxZ{rN&N}O?01e1|r^S8;^fk=7l&l6! z01n{za5Mxx!Q96`{&B#r0eD+O7aX2+(n-p)L!bMrU;WBwt z8*THfz~$Q~2K^JC_ymj9Zqjd~buH*;U+Y@e+Wx9E9=&S~RW2#OnO*}L{FbVj&PicZ zy@uxaagTdkSI2ghq5-}IJ-`pm^|8kuOOpZ4_X@IXW6u3{AAqn9(Q?G3w?&v#(*h99 zrNOqxe~$#+aq9S@eQDVn-tdNKKENjeT@(*cfua<&x?4f9c+rbsq&|0d@4+s=wnqwj z^rbI-=@68P%4reJ8fx>Gzx-u>bC}Q8SaX+Q$eMW2Rmuk7nD&6ql&WwM5L(SzDALdr z4ドルj#a%BWz?e)ncwjDpc_{#q&3v-nQf2q6&Mi^c;Icz~qgK=+#fI97JxcOv+)fFtb- zU-*LPT&*3ThG_c&;ar(V3;NRf++)23yCxg#Qh+a2wKDb$Y)r&(0Euw-rfBPB5XcXdL(cHIzeC+lB$LL zmX^^_*8yzGu7rWRsPXd77&+}{4Y&-k14{#Nu7Jr@Vtvqq9%QzQXgz?7;DJu)Jd2=A zB+g&kXTk)(?|uPt4Y1=XqU{-2sp(QmtcFrub0`35haP(9&^7g2BKVm1^6A^HH(;0M zY*};;Un+FreV?^xZn`O^Vcv}77@*Kbg%zM9-vw*MbZ&?Mu_DYoYycd<+axof$affn zIyV|F4$uebW$x{nbDowIuzj|3>T}EWL9;i0%wryN5-@4dJKph*08R4{V1Nh&Ge>BU z;mQPLB3EGBCjcWHN!9vk8YUw99B1ST*n%ikG#Z3KYnAw2LX#OU8UAD?SQS1$z{ji$ z`j~FV0$Wv%r2sx6I1wVhxg6*8nq0qqmXogZJiVm#7(hSrk&onij4=aw4N_=f(rMgI zJMF}jGQa#he@X}ta}3O+Yymu}oPdmG5fmCt$bf+v>x^njlUXkL%@ruqrZ^M-E`u;k zI8G>8Wl4xEFIJ0iH zia?1b^W#3F@jeAh22xUi^T{%p;o=&!+;%xRbA5S02)iqK5q=1a`+k7Z+08Y>*Ja;_N)^@@JOJ|fWEdd-Wr>~CZ5_~5h z6EwUdByH_|_jLw#N#&+RXPL|=jiGS>WzG#IpqW6I6RT|tRQTSSdQ6K`YIPkEv5AUi zkPHK{yWQlQ6$O;XnY^{^ojso!_P;%N$y*?Vp3DzPHF& z{mv}fbe#Zxm%H5Mhyq*?0yN<29mi4>(A%h8)eXg0lYYOYS1MAfb5c9SmT1p7N))UD z*5Bt`R>XI{FkrVNV013OOaLYUbt0mXOnRDFq}-tGVwafxX=?A%*7hDpT;X7sFWIM3 zjY||wm~ws@*tB*W=%^2RfKOq@ry$INf_J^^U2ug`Uqjgqz3KI00TBUh32LefQ}Ui{)01644EfISoFeSaPeCk&aV zMymyMVV2L9Fghl+1v@4`k$=l@RXh>7@6tEfgyGFx!*|<~+6m<10&)$rw#rtx%fq>{ z7W5QU`p?@p|16B09k40T%oTL}YOlR;t%F^Rym{r%7Xh397CqB!QP-nN#xa(rgD4|euDk2d>6O2IzcV?@vT>AM_RVA|}L zRuMG634mwveH`^cr|Esr{hkJNuEBkC-HVEDzP5e$QKX;QR8Rn@oDu*%XtTPD2sTnQ4H zcv1f(&)qlgsk>huO_v?D*F9>jfn7d(`=S;68-J+y4`?~0PbPf~=p&^ZjNRp3_ zB>Vb-oCa_-%BTB2z8So^w4FGyqG;_*nkb)joD1{c%Ar67c8FOAdJp)Kz8u)cAAh`v z*FcHEg)plnOaxZeYXL_sTLp17pLOC|@`8-pcWCGk#NG>d3RQ5H3 z$I<}1uqeA}l!=tBBm z%c%{*V21f{Q#9Hpb5PY&2F|?#I7#r(?Y584_JFR*uLfY+*eCliSa1edVCUHE z&-`!z&}<6|0jb(7hasd18b!q=!4t$-l?w%$oFlDRT;PvQ7)o*-xY8 z&`c}s|L%9c+g$+-0E7ドルh+%qJ&i{J%7c|PBq*)8^sqZV|tc%tQf&;xkBqfyJ3@wJ^H zv-z9=uaxG1OzX9-3aCsvz@z1(Ku4{n({6LfSP7<2q_4vmhx$=+g?{6p-m)*kji+n| zz@|ySHbg2-xq_RruV0WeOBOm&HMmGq{Kw0)-^Yn3h)AY zrwXiAiEvfb0(K~7J=Bld13K+C8&wrdL8HxkTe1y+G6BZ|m-e>D(g?m^j*RJ;Y^-=4 zx6D9AES3S81a=6>Ko^uoVD)1ZwV<0s5ijh|@kh2wb_`)$ri8ue1$fn-7s)z@-o?7i znnXyuL*qbi&AQ*}*eTisz)_T`K>Q#}5G`s+z(kT=OI&azHS^#xH(K8I3&^xxpvkXK z0=jES2?cPYv>eT5-lds%mZ?*VCI-9w=0;V?I-PIK>{4JO+C3ZS0bD9ZyZLTJE5i_! z?_mfaX3ドル#2BuYy#B*!{mUd^P-sjtjtf&-zXu_2~wH&wl96b&&(Nf zAy^$db&!PNfzd`&!HK!&wvVxlT=Y(Ei}*v zzX`BsCa3|}IX+u*Eh%eWI26!lTkPO>J6@im)vD`V?|PHHTc(C54_5Ll;B|^tn->oB zv6>#RL(IA99ドルdx+?bRuM83Eijc{ULFYz#^D%6{e>=mZ19_;%Y&ah|40*HR5@^Z4Y@ zz%n6W+GceNixXdtJfn$0TR*TXfh^wgCZauV|ys)SUplgNI>|-X`*t^kNK&_ zWvMR!Fx@tc2HITQgweLt;nm*7I|MgcoIWJ}u0ドルKWyzCF9@26c2!`E5Uh-m zF(Y6AA{>)8K^-9p$ow0C-6yL`lx}?n7%Aj5&5|Y{dA8!6nt=Eva(h*uiw&E1e z)EDsZg1ドルzSA`2ZSHSa04dItUUOy}3zB4?iWR=#Jq8W^|?;D$i~3cMLWkA{c%Ze~|> zE{AvHkzh0R(J+-F+H0R~CrdKm-h(PK0WhKzN%?Nnaq8D)d zOqEH5fCczWpvSZaa2l?Rs@Rh1lLtWYVJdi-oP9bT&~;crg}|gB18`vJ^d!wNtm`w~ z$UOqSXR^;k3Bgs~(c;-y0r!}+8Jux_8r_b2ドルXuX*JixYHe;osK`Fmd(GMTcJE-P32 zfX|qxYnCswU`1B{yPO3ukVNM*dd_I3(lijIptFIVRja8H z5Hf;?^_&bl0fS1YKyGY(^xDCMGGSq?|lOD6q zXUlbs=}NKO1XH;L!BGHD02Ru_1?drFps}wcAb+oX~6awKkazmftY_X zuw5|d8eEC(fJF(O92e_j&}S;Z#W~yrpa`9QXFgr}oWKlttS7MbBbFx=#n7j`m;gWZ zmy2?*Omm(=(^CcH#|5OWwf9KCOFQ?o@`K;8_A9T(MA7hQIr9x@9bg7Mln3OOXt$EA z?K%Yn8q_BmAYE}R2Iz41MfzP+0H(H{C^i(J1$e9h^9@i$I{@h#w!4O^Un*Jvk7lNP zOYo*_`YIK3T|!AwJhNxj56|h7Fk!w~Llh%@8AYC*ptDFoLxkIQiUA8i1AZjv)x=SN z&i}aVHxlsj84Km!S>V^#ZeTOtd?zb_$sR%nJrvDKNX5Z6`b!WqWGsuE9()m%ydP3GjdpOwYhf+wa-j2e>JFx?kc1Yrm#x0x1H{OlM>< zvsv9YGjd!Xc-D%;t z*JB>fck{z4NI(wg{I&UAEP#9%(9BKyFn%j22sQxjceE8b$L|0oA`eq^5*Yb!k)&q; zjH%<#-8)goecarugicjd=ndgm24yz3xtzcj%vs3eqy=}#uyuebz}6qyb=~zf_jugs zpupDu+>_mt zL9kIKgrGj~`pTL)&Zr-YlU6Hx@PPsfTo;dfwe>Rd&b@!d95ex%)eX`sAjf=%n z@nZmYyax(lMBajoN!cC(jLv%op9$;`M);(;CLds?@3w`}G2_lP>5zswFC$YZK07gl z^m7i){Pq-Ekn06Z&y)odR*Dk7et#Y3JeH7^A`IE5AB2+d>jPj%@!NKkV3v!_Sb(=@ zaB)B<5x(jauitg%sf`8ryt@!kel(gh3jvts%eoj6<^r5r4$^l##kxkaktdr1ured$ zSEnNJiE0IT7?c;~Mr$Ha?m?hvjI@uSIxiJA&*Z+M=DC9HeJmN@r-6i+u(pC5Gp;Wz z4I#{6O`cU$uMUvcg#~N`Y=AkB<(!g{_9n%x84c?~$|u+rtgajhc+iw04d?>hK{2x~ zHW7JHQpO!9jQAbG=sZ;NW1z=WV~h$Pzl1S>;*Xgy#|La?Jb_(C+8iGyON-(PJUhX0 z&jds6=URf(IWz%MM#GXj6F`NI#2RrY`fD5Ekj(%ukvOoWX|ZCCX%&RKkbMw_q@c4= zUZa8^c1ドルe9;=$^XfY;=_mB6B*0)~J+4d4PP=^8)oe4U{AeZqJT#gAp-9&2KII-VfR z@xW1ドルq9rID6U?~=H$vmGKY^lSa!=dzZ;s7##&5@!1axBugf3vGF9DP*0=o1K*b%}$ zrQkxiMfWY>69Z3iXt?Xt?BmfIUO+7qt%}ew3DbZ!A?cuCO0dU|$IOpCo^&%?{H+JX zm{ura{$vd_$n}$thB+9|EDF3Z)pc{<42mrpw4yc%yvtf>bMe{n+wR3h0FuJEXRj|Y zxAx1W6Pb@9eM~tP;OWDpRoeSV@q5{V?(AeaSJ#wr( zlQ!@(xnEx(SPX80VDQiJTQOa(A%Nqr1t_i^z5ドルLhBXBWfI#)AJJAoabe4+$}tc+-! z6;i;7bqN4|GvffzU#=<{pficaq5-_fq|xi#e|s0qxlkdysLg^Ub^0$$+E}Dis1f~RRQrMbcfB_vyv_!t{nqdU9 z&nICN$rt2dPVS#MZ?>i53PmbVb7&SNdL9ve%y)u8_hvSc8hxv=v2;Mw7ugQ538KC_ z#(DaZluLi25ctGe7}ab0hbFkm_-_8TP%We+bR^zU}fgo#4Gd>Gg@pP?RS00 zV4zol&7qkQ3&e~~yJj2Vqm1IE0OWg~qs@tF7d~43iZA`X>ZAQKk!ZR;vrKKR zfp$rYTtlCj*YF~hGy;=BC;dp=;at^D+MnXoOw`B4IfAwPdH#w5ee`&E)n7L$%~;|+ zry}*7tn(viq!3_;YMGfJMr>%Z4y9xH1X6-9*8;X^Jf<1q%o6fhksdssylmw;pb>H= zQ?lmJ3^*9JbZ-J1OO^icwX{^~l?qx?J%D;vefA7~TZj+~6yGi$p>6i-m%iXZ6d-(| z6ozss94(E~%|?A(+>3?vtV_ybZZa6x>RqR&culq7ZHD2^v4VhMw1i1TOgO1cr$PbU zu^2=PFtHsV@LN4V0fJ`p`za~_8*}^?jT+jVLo>t(oYcC3olGLk(&+z~AZ=gb7W9J!(w9Ei$Zwi&wy-sMGJ&LilTa&wz&6b7UMCM`YBfjfXekUWXEHHj zQm|1vZ{P(8n2-p&Pnlol!7{_6?R?C**Lz_i!Eqd|4(J#oPoAC9a2i zVI^cJgmlJPtNi$a{N04qKxcKPM`N`o zm1ZZ$P9DzO@wszpX6w+{6j6W^H$ubQBdK75AST^5rQnRh#Ui+lG)%ClZz62qIzD|h zHetKp?!j^a+i#XC8gF>oL=%=GL%&lH1(o9y80Br%!I|f!*PQ0v$`oG)bn~Jz&#Xs4 zA4|?*qNQeXavjt;9g7*3ql(CNQfDy{=rkIG_(Nc0zWpL8ドルtpsNYAWuuO`ilxF~d!6 z3%G1={bDpM{m#&8pC9@ZpY54}OGsTOVC!SFneghX{;(cC9dGV|*at5%O==pVYyvfw z&tHFDR)a({uRN)xJeyXeettCtm_*(>+9w?yNl9na)azQ(1KI=Z{G(C8woQk90+e(r zb_i;sX*3^U$ed=TWHp8W&@~AYVnIUr%^ZdVXh3Nx&q4r^ZJs@YI)Kx6$MDtqN^m2r zjt%8+yb%6~s>{NfS>zg-4Koc9PA*U!z{;LqF~jWdRIfnh1m?J+c1$K|6N4S1<@|`c z$ZQOt>KKLtRflXtZKs(+e$aj63Nk^_WDISNCDQ!mwR;5ku>^3z zN#?AJP<2{o0vp6o@TV=hv7%+9o)nVJ+YoyB;=gmIM%!o*AaBl)mVLLJEyDw!7&9b>%0nnQvqK6*q|itRI5SrufR-n zPlNm4`xNkLF|#hfVa{|oDGs0mI_B60Y?ej9!yK{(>#K$nN}8Eft^haS63~Io%o7ZP zn|`xulnLMhtiHP(rRnW4t?Lvw(!F-kC(KXIo)7`8r|bmiLA%L8fv zH?pS6sEDK=Ot^ir5=ALIFh3q&=8}dXR@eG6L%fPWg1^HCm#0-J^EL(0#UjYkcoiht zI}O+&)N4R{I&X`(xGIXb z8C>306AO-$+nJbX_GOmYPD=d>oKYphQ`a?}D2!^EVp~9`>G>yr-xI}SX7b#2@jJ>0 zR0v}-HjVlV^D^IkjX88E11wqg)DE~H7ドルf}0z|Q}t*pBl2c^#U0^sHG@*EtYttH9&s zKfq?vj~lFwR=#ul^1UmhRc69q6zQ50?2t|x2R9JHhe=le9L9ubzCeza8UZ2*EkywG z$`6*nIibV`gaQDB$M<9izh^2q&*b=4pz;yw8zefw2p6(1x4;uek_b;wea{o}ysu%z zmpK>1goksXte@v<`e9ocmu`f^afqob_1uacptuy<#>pq3u|nWFK(A{vb9s?Z%P0U~ zH1jx0C@`Iw7ドルsO2Mk^Ev1kEtbN{+tZ#NVoz0WL9xeMun+o{5F5li&Sk1-$-vj>H|I zj1Ab&qIr(+BK(<@kl?gn;go1o%#c}jwf`-pftz&kbcidttb(zn4ko4ta3p28f&xh2 zeF$c`cFQo#6~qBP`w`7FxWi{Vh7XpH<`}>z-(s@g^5xX$BQUN(*a(#KT!Y{MJNtp{ z8h(o-XlFppeE{tm&daMrkz+*UER=%}8U-i;8~}4Xb1M3$*?gpTy9Ge! z5gf&wTthBQU=kc+q-ziy*I=ey1Bjj>&*2)bnd!;`Lf3SDp4oTz>T4ejT2on0vh?$imr_T1l76G@uNnu7c zjzGy#X*1?VTaf#)2m&A91451g7=X<_$pidr>Y|6b}QYrI223K^e66X4sQB1qE z37`NX05S2jN{3vViKnGZbTkjeRPwE@Fyy<@dcf`w0b2`l)}+yf8pvw~ziyd?wss4c z1(<7pe~8h!a2_!ki{q2u(7a>1=JHTdLeHfDCGL^h9x+%Kt)|axTH1Z4KnpMerwP}g zd2o3zu$g2;Lo#_V{{}>mXu5cT04T5o)G!thEwk+xm<1q=xkrb!^d@sy_xhms%E*BR&gIO0fB7B}1$xIRX&?m=`lT zj)1~7m_mioec|xj1dm^54FYzKBQ!qyK(Ja5ドルa_%dKZ+;;%j7K> zO0lAIz#kY4t_;wRkx~KO;z6Kg&UZt3@gA_5=Kziwj>j}0(zd)gD$?yc-!6y}m8vdT zk}PNhBI^S5Xgx3jRKTJkd~SbVdrW}#d(?5RK_GG+_snDl<~#jt`%dm|aouqvz8)gv zqu%n6Y0un{ax6DzUcoU}8rVtI3iOkbN1Mi!WkgLa8iN9M$Zt_DCaXRX0_9DD;ry0y zd2=#<%crke|61&a#_~~p<+mnw1e64kkkt4_j_hxn2cqe{!$svp+4noqj3!lmk47-d z&eRoJzujs*)82nIiasl0b3rP6Duvw5N%dU{cBMA8_ASe&pCgY3y4furHd0IanM^ZS zfSln%U<28#46@!!u)_*CM}7~;(C~Nu zUzNLINeGp5nvplwNFQVeo*zi<081ar{he0j=&~(q?oms5usln#_vvt7ドルcxisot}z+ zIaps>MULB*A78?!105K7_gGd^3f3?{$V1yU#ue;$qST zb^+aU2k@L5u#?3o>Ubt@Ye~^|gY~UddN~u%T5n8_^=>F$|oQ zTJTf7$}Ctu&r%zKUj9Un)(iM-6K?>&o>vg0NFx;kx>75U#S$z)7C7c!Eikl{X(w<5 zBEzdK0Xcvp0)P^<<<;yolv#qje!8d-`m(fwm%;klylem}k$ljd9yb}h_pts3-cdu^ z_#{uAgn$sY(SqQ21i&_$#D8b-g=uE4X;4zJ6i|Rm=$tpoFWcVU9ドルV(khC#DY-V;ucq-umI%$|t? zXeLkk48S?24;b>?v>327k8jU!-+?X)2V7>=^vU)JV&6&;1i_+Mfol`2om~Oq_JgZh zD=@Yypicz4vhW1+a7&2qTz-W_l;P;)?)G1RF-rcAiwr=8ドルP``e~nj+n(nc^(?d# z5CIs`aK1L%642?}G{jYa<@o$qfc4n&(yj%jv5#%vx{2ドルa3bvljk0vujq50w zCu8i#ln<9@u-ert_mj?`et`olmo4w!fcs>NFeXf7_8do(RGet25+lUW~snaFh&VN8z(uwIdXDf{wxt~7?gk8 zxAv;s1M`~^Bb6`aHi9yWvT9TH58&$knC-f9Xx9a}h~IT)1I#f5G++}tnMUq2jrp4SJFA4l#Xs?Hzk($ox(_K(l%(OfU@g*m3~pEGyh7qXvL6gyCWU zk-2heG*X1jhudy6DjL|QfZL~R&u1nwW(+>_WzbN?NEOG*z)Vk z^MB;ExE=UF0WLF~_o5)$H4c!F$ld^ii4=eZMAoF!e9^jrifCu*-Og)2Tgq{nP?1qL z*Ax_rz(wNA>t4s!=MIDUHY4!cTD4l*enEk4-nIGH#=cE{F!rrrB5xJ(CbCWx+y^I1 zbXz|f5L>_7KW`M+w3yZk__z|!fHGDtr?&5ciaQP~(6=4Tx2dK?Z&7}ILCtbGC9fax zc1Q1zGw$s*-z)*MEXp@90*GeXQR{yr3}eaukl&dXU*IXS7}#)+tVs1e)iI#2D!@;v z;}(=gZ!wr}m#-DDI~VAS%EX^m+P>1bcfBUmnD=loT`htq1T26wPe3R47WHSWJ(D1q zUM3i2IaKQy;zgRQP4P+c#N!Q92RW*^h)J`p3)lOi@w~N#jVASU` zAwovLzY`pQuP(sPt3t~OB}9i5*gKY>Y&e*2>#o)NXErNf_b8ATc=dNpb2_;MV2OWr z065AY_f*br@`D(F8<3(upbk;tc!10asd0y1vtldwu#c|n>;hxcrQMqi=G*$!{Ii3< z^C%y`SG|R5fB)80fFEDc|6)0RX}W)4TwR*JvNU~JY5L+y+m0`=7uK-r{-x=A70}xh l@S6_*}_m6jY4)|aFD~`4bXjtC%zvqvnVH68g(l0-NWc(!SMbh2_+{{2Y5Do^319aRVFgJ+y2@pA61q0}} z{SG^BKy+Yw21X`k2n*}6Lk$-|2LglX=)nvOzor329FGI^+zcnglyn(+uDddc2kMT@RMpjkrg1W}VOINPy8yH?QGPbs{g(K{4 z*t`GX;d$$}mpAfWP;f}-eN_*#)HGar#pO4-U}j`wVq{=uVq$_om|56(+1XfG*#u7TaPkTZ zikuM^6cQ4ZRDg<#%s#9envq4qd=mj~s)|4_yhkkkdx6f_(~gsxap@)}e88h{_exay zLSoAQXQNFVUrT1%7{CSw9bYCeHvj|n4y5xQu>7v26nDpg>eDA12g{S05e;YT4nTvM zW-m)*l>);n5S}sc#zFaQazbso<@x$cb8;~vwuyt+i_iahs#@~ecbl=f{|yvq{bh5v z4La7iMx@SAk!s^xj>gFJXYu%vhh&75=^yR|LE2pkSKxUI#rufKp1e6SX8PLPx1bWY z2%BWsr7ZH3)8FBB{R*poGtQ-n4gMqjO?Pfg9j@*V$EKA!EsaH^VzOd9AFoMN4ドル@v6 za<4e92%r7gx73l#3y^s(akq)lw4rme&zd8@qf{et9ドルv%=xnbtiwDE>YeLWFYV|v5WL}7lm35c3DH7IY%EB7=R}-(;bZzk7 z4aytLDX(1%S>MceamF2Z(SWys@Tbo^L`MvOA706GiS4DVQ@84%f zP$$Q43g6?4fL=>-v2l&_Af9_j^$X*eFf(1qDerzmzCV%~eexAPu}SP38Lr|BIFy-T zxT3Z`!x|TB2Z{&`!$GTt&domEdh-hzi}V{@C}}FU&Dm^+IOiJ$_a%enyB4M5h(>E( zY4dP`EcVNxJco+~yRce%fNmk!EC->qoM-868EHTR?$H3*a(16kv`GZgv2s8{;P7g$ z!zhzHT1+qAS5jS#e*Rq2dOb%z2WDDWaBOMD^_GBKb{2wrw$qpYQ-_~8tMBXAGaf?M zVD+=j6+}6n)<}s%!^ebz=pi-5zs_izn~kyfpf}+-s6@o#ooog`q=mdfg4ud0lm@x_ zD|^J7jt-qZnpc$hf>tTIYqE@@EF*QPj^$E|!khtiBiz;JdSp?GrQ&NSmG~#(;i#J< zgmA@82d_x}%!?w62ZH;PMrXX7jRamy#H%Q^yP=Z9Qs8Q3-s?_7Yig{c>fI`9I!`pE zcfk+$_lmud&gSAJS&i7r&W)sx1GR{EM0;cV1?{WncvCHHTLr!{ps){BH>anViIXbz zmnFGW;kx9WJ2c?^a{wXngTP`o(;aZm0N$^qJ&2#M&|RBgn(-N+o>%dTWE&_g&F8j{ zcxhS825<``p%r1we4gl`l>)|0hFfZ}Aq)P~&h{#aTC3$|bj^*`PvX&JVcn6r#q9NG z5k%}5WJ!jt%+78{A>PwIagOb!9UZ5ocZum9zIGyrlh z1s3Bs@9}kI{{n?BhnhBf*637BkM+~EZEVXo1eJ@qpzWaZ*>f6jrfs{tULB?KsY}R} zPkI$e>)$)ra7m1W`uyd2j)jTRn!$O2b21GBrqVT+gdrLnP2cYJchP`gRfWh0o=wKX zlTOCeSbg(7g#Af%#@oZ2G>0vW zl8?%?kFzgeQhRPttT&t;ljls1f@3Ty;=}uqDSOw$s!*VV?Lsp8V?8ELjkoFzCM??t z%PL3%?qgjKvWJ%1!(N3Ne=rl#O*PTn40}xjcuQe1oF2j&)ca}$GL)h_NW`aV$_s6e z{rk-;)|Gv;{f)ve%hL+elJzyqq3e<_+g&6m9ez=x8afgq14p?tjt(?shod9e18>>F zCMn)BYGUHbDt{=7j+hW$;@V!YH4W@0hv4?K`W_%e13|OP2Kw^L^QN0Adk~k-C()S; z6+cPekT$+1^_gZ?23Bcp-xJ2$vY%r}gu*7i@091$UsGXC1Sn35CV9TqA-F`nJZAs;YFEip! zhZ4LuU7oo2*7)3(g4Nyr*Z~_I*QKa9(tyWD%6)#xs^`IhPZ@QEOP!haWuf`u2!l@#yA?} zSYg(1X2|FR_oCmISZ(=HX@4ctm2OoI5{|qaxi_+gMQt~;hKuy8 z2Br%(l;;O>T=fY4P)M09qXBe1y$I;0$PCYvlVd2_++X|y(`^rld6hH`iBS-}dy}@@ zEqz_2DQPw#`)p(s5ukAdNKNb@WjSMFb$}g%9*L-tW^X! zfFGc-D$}|})oc%>v7hZh zSpPVoYyt-g~dqIfGSXSp-^NEX{S@Ql0R5} zZaz-i!V0aGxuRuK#9!fX7d@;2UAjmOE!Qu(Xx8qOoqd{BkUQ8R=}~8Ik_C_AO1G5a zu;%m0?>rFQb!?fYn186ドル-_o`GRPC}_`OdCe-!or>g5MV z7xl)6HnKmg`t;Ab-C24Qrxr$k{NZF+lLDxGJC6w&;rxCIjYfH50^Q@cw%Vy_+vM_b zg|02+z5>dpW05x0L#~z-;acCLOeqLAo8f0OXR2CJn9mdErZ*XI1v&eO3)CALvnZaa zaTrz8`9>zmyTlCdliqE@$=DESFDzBFpE)Xoe}2lpOk@0Tn7ynep>i>*XV&^O9@UcH zA76jE$lSATc-Lf8KD)!lVS`@F>!ON8N^ zToK&TW{0toP`&uJD-MSlIc&B!6uzU0{Y&_1)$|jAMwfhP0f<&y#2d_!&3ドルv5qz-dd zo`s-6VQGK~bSbY1Q+_17CqTM5xL@27j3xHahIctng?ftckt3aS47j_PlOfl>wli70 z-{P!LBb^}>xZFmizUALtW&ad$y#Z?(h7D+&ZH)-IkvW3d#&eCuN5DVJrHb5p^HFRE zE)=z`mS``F+R+v|=_(Ja--+AO`H3{QGMd{axNRDK=w>pTI9IT@8TIv}_&bq!8CY2W zDkN5Z?flD!3-89FZzevY0lhv?E2ドル~qli1dxv#YsZY7Bfv@96L?r59^;c3-}ASjI&I z#)A4C!cT7$i^>Ntfsn#N>v-Oc)ogVTO91;m7Ymq*M-oHO6CsjOq(AfOJSdz zUSV3APSxJ*3q_m4g_&!PAD?W0yzxLT4X}0f5jp(xH8stIb>dIb=O$h%X>Q3Mn)J4f zr6WRup&936&a8Ih@@U><^c4~vpr)@6+v$xwwhsm@gts6wqj0sn^kdcr$*dez!x|o6 z@wL>DFxr7V3gaViMmHMD{vZb)fd?->MynIhc_qY(HRvae>^QVbz&woOh_#4}ZnYwY qT<{zlt@_tsiskos9et^-51q^o`km>8O0iI$IGlL+7JM5<8~qpnrbf^j diff --git a/docs/images/logo/ruby-logo-198px.svg b/docs/images/logo/ruby-logo-198px.svg deleted file mode 100644 index 59cf324f..00000000 --- a/docs/images/logo/ruby-logo-198px.svg +++ /dev/null @@ -1,948 +0,0 @@ - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/gemfiles/README.md b/gemfiles/README.md deleted file mode 100644 index 1ac3a713..00000000 --- a/gemfiles/README.md +++ /dev/null @@ -1,106 +0,0 @@ -# History - -`faraday` v0.17.3 is the first version that stops using `&Proc.new` for block forwarding, - and thus is the oldest version oauth2 is compatible with. - -```ruby -gem 'faraday', ['>= 0.17.3', '< 3.0'] -``` - -# Ruby - -We use the Github Action `ruby/setup-ruby@master` to install Ruby, and it has a matrix of -[supported versions](https://github.com/ruby/setup-ruby/blob/master/README.md#supported-versions) (copied below). - -| Interpreter | Versions | -|-----------------------|------------------------------------------------------------------------------------------| -| `ruby` | 1.9.3, 2.0.0, 2.1.9, 2.2, all versions from 2.3.0 until 3.1.1, head, debug, mingw, mswin | -| `jruby` | 9.1.17.0 - 9.3.3.0, head | -| `truffleruby` | 19.3.0 - 22.0.0, head | -| `truffleruby+graalvm` | 21.2.0 - 22.0.0, head | - -In the naming of gemfiles, we will use the below shorthand for interpreter, -and version. Platforms will be represented without modification. - -| Interpreter | Shorthand | -|-----------------------|-----------| -| `ruby` | r | -| `jruby` | jr | -| `truffleruby` | tr | -| `truffleruby+graalvm` | trg | - -Building onto that, we can add the MRI target spec, -since that's what all Rubygems use for minimum version compatibility. - -| Interpreter + Version | MRI spec | Shorthand | -|----------------------------|----------|------------| -| ruby-1.9.3 | 1.9 | r1_9 | -| ruby-2.0.0 | 2.0 | r2_0 | -| ruby-2.1.9 | 2.1 | r2_1 | -| ruby-2.2.x | 2.2 | r2_2 | -| ruby-2.3.x | 2.3 | r2_3 | -| ruby-2.4.x | 2.4 | r2_4 | -| ruby-2.5.x | 2.5 | r2_5 | -| ruby-2.6.x | 2.6 | r2_6 | -| ruby-2.7.x | 2.7 | r2_7 | -| ruby-3.0.x | 3.0 | r3_0 | -| ruby-3.1.x | 3.1 | r3_1 | -| ruby-head | 3.2 | rH3_2 | -| ruby-mingw | (?) | rmin | -| ruby-mswin | (?) | rMS | -| jruby-9.1.x.x | 2.3 | jr9_1-r2_3 | -| jruby-9.2.x.x | 2.5 | jr9_2-r2_5 | -| jruby-9.3.x.x | 2.6 | jr9_3-r2_6 | -| jruby-head | 2.7 | jrH-r2_7 | -| truffleruby-19.3.x | 2.5(?) | tr19-r2_5 | -| truffleruby-20.x.x | 2.6.5 | tr20-r2_6 | -| truffleruby-21.x.x | 2.7.4 | tr21-r2_7 | -| truffleruby-22.x.x | 3.0.2 | tr22-r3_0 | -| truffleruby-head | 3.1(?) | trH-r3_1 | -| truffleruby+graalvm-21.2.x | 2.7.4 | trg21-r2_7 | -| truffleruby+graalvm-22.x.x | 3.0.2 | trg22-r3_0 | -| truffleruby+graalvm-head | 3.1(?) | trgH-r3_1 | - -We will run tests on as many of these as possible, in a matrix with each supported major version of `faraday`, -which means 0.17.3+ (as `f0`), 1.10.x (as `f1`), 2.2.x (as `f2`). - -Discrete versions of `faraday` to test against, as of 2022.02.19, with minimum version of Ruby for each: - -* 2.2.0, Ruby>= 2.6 -* 1.10.0, Ruby>= 2.4 -* 0.17.4, Ruby>= 1.9 - -❌ - Incompatible -✅ - Official Support -🚧 - Unofficial Support -🤡 - Incidental Compatibility -🙈 - Unknown Compatibility - -| Shorthand | f0 - 0.17.3+ | f1 - 1.10.x | f2 - 2.2.x | -|------------|------------------|------------------|-----------------| -| r1_9 | 🤡 f0-r1_9 | ❌ | ❌ | -| r2_0 | 🤡 f0-r2_0 | ❌ | ❌ | -| r2_1 | 🤡 f0-r2_1 | ❌ | ❌ | -| r2_2 | 🤡 f0-r2_2 | ❌ | ❌ | -| r2_3 | 🚧 f0-r2_3 | ❌ | ❌ | -| r2_4 | 🚧 f0-r2_4 | 🚧 f1-r2_4 | ❌ | -| r2_5 | 🚧 f0-r2_5 | 🚧 f1-r2_5 | ❌ | -| r2_6 | 🚧 f0-r2_6 | 🚧 f1-r2_6 | 🚧 f2-r2_6 | -| r2_7 | ✅ f0-r2_7 | ✅ f1-r2_7 | ✅ f2-r2_7 | -| r3_0 | ✅ f0-r3_0 | ✅ f1-r3_0 | ✅ f2-r3_0 | -| r3_1 | ✅ f0-r3_1 | ✅ f1-r3_1 | ✅ f2-r3_1 | -| rH3_2 | 🚧 f0-rH3_2 | 🚧 f1-rH3_2 | 🚧 f2-rH3_2 | -| rmin | 🙈 f0-rmin | 🙈 f1-rmin | 🙈 f2-rmin | -| rMS | 🙈 f0-rMS | 🙈 f1-rMS | 🙈 f2-rMS | -| jr9_1-r2_3 | 🚧 f0-jr9_1-r2_3 | ❌ | ❌ | -| jr9_2-r2_5 | 🚧 f0-jr9_2-r2_5 | 🚧 f1-jr9_2-r2_5 | ❌ | -| jr9_3-r2_6 | ✅ f0-jr9_3-r2_6 | ✅ f1-jr9_3-r2_6 | ✅ f2-jr9_3-r2_6 | -| jrH-r2_7 | 🚧 f0-jrH-r2_7 | 🚧 f1-jrH-r2_7 | 🚧 f2-jrH-r2_7 | -| tr19-r2_5 | 🚧 f0-tr19-r2_5 | 🚧 f1-tr19-r2_5 | ❌ | -| tr20-r2_6 | 🚧 f0-tr20-r2_6 | 🚧 f1-tr20-r2_6 | 🚧 f2-tr20-r2_6 | -| tr21-r2_7 | ✅ f0-tr21-r2_7 | ✅ f1-tr21-r2_7 | ✅ f2-tr21-r2_7 | -| tr22-r3_0 | ✅ f0-tr22-r3_0 | ✅ f1-tr22-r3_0 | ✅ f2-tr22-r3_0 | -| trH-r3_1 | 🚧 f0-trH-r3_1 | 🚧 f1-trH-r3_1 | 🚧 f2-trH-r3_1 | -| trg21-r2_7 | ✅ f0-trg21-r2_7 | ✅ f1-trg21-r2_7 | ✅ f2-trg21-r2_7 | -| trg22-r3_0 | ✅ f0-trg22-r3_0 | ✅ f1-trg22-r3_0 | ✅ f2-trg22-r3_0 | -| trgH-r3_1 | 🚧 f0-trgH-r3_1 | 🚧 f1-trgH-r3_1 | 🚧 f2-trgH-r3_1 | diff --git a/gemfiles/f0.gemfile b/gemfiles/f0.gemfile deleted file mode 100644 index 4cb7f887..00000000 --- a/gemfiles/f0.gemfile +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -source 'https://rubygems.org' - -# See README.md in this directory - -# 0.17.3 is the first version that stops using &Proc.new for block forwarding, -# and thus is the oldest version oauth2 is compatible with. -gem 'faraday', '~> 0.17.4' - -gemspec path: '../' diff --git a/gemfiles/f1.gemfile b/gemfiles/f1.gemfile deleted file mode 100644 index 94cba5c6..00000000 --- a/gemfiles/f1.gemfile +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -source 'https://rubygems.org' - -# See README.md in this directory - -gem 'faraday', '~> 1.10' - -gemspec path: '../' diff --git a/gemfiles/f2.gemfile b/gemfiles/f2.gemfile deleted file mode 100644 index 7c3868df..00000000 --- a/gemfiles/f2.gemfile +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -source 'https://rubygems.org' - -# See README.md in this directory - -gem 'faraday', '~> 2.2' - -gemspec path: '../' diff --git a/gemfiles/jruby_9.1.gemfile b/gemfiles/jruby_9.1.gemfile deleted file mode 100644 index fb2b9158..00000000 --- a/gemfiles/jruby_9.1.gemfile +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -source 'https://rubygems.org' - -gemspec path: '../' diff --git a/gemfiles/jruby_9.2.gemfile b/gemfiles/jruby_9.2.gemfile deleted file mode 100644 index fb2b9158..00000000 --- a/gemfiles/jruby_9.2.gemfile +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -source 'https://rubygems.org' - -gemspec path: '../' diff --git a/gemfiles/jruby_head.gemfile b/gemfiles/jruby_head.gemfile deleted file mode 100644 index fb2b9158..00000000 --- a/gemfiles/jruby_head.gemfile +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -source 'https://rubygems.org' - -gemspec path: '../' diff --git a/gemfiles/ruby_head.gemfile b/gemfiles/ruby_head.gemfile deleted file mode 100644 index fb2b9158..00000000 --- a/gemfiles/ruby_head.gemfile +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -source 'https://rubygems.org' - -gemspec path: '../' diff --git a/gemfiles/truffleruby.gemfile b/gemfiles/truffleruby.gemfile deleted file mode 100644 index fb2b9158..00000000 --- a/gemfiles/truffleruby.gemfile +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -source 'https://rubygems.org' - -gemspec path: '../' diff --git a/lib/oauth2.rb b/lib/oauth2.rb deleted file mode 100644 index 1efe7923..00000000 --- a/lib/oauth2.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -# includes modules from stdlib -require 'cgi' -require 'time' - -# third party gems -require 'rash' -require 'version_gem' - -# includes gem files -require 'oauth2/version' -require 'oauth2/error' -require 'oauth2/snaky_hash' -require 'oauth2/authenticator' -require 'oauth2/client' -require 'oauth2/strategy/base' -require 'oauth2/strategy/auth_code' -require 'oauth2/strategy/implicit' -require 'oauth2/strategy/password' -require 'oauth2/strategy/client_credentials' -require 'oauth2/strategy/assertion' -require 'oauth2/access_token' -require 'oauth2/response' - -# The namespace of this library -module OAuth2 -end - -OAuth2::Version.class_eval do - extend VersionGem::Basic -end diff --git a/lib/oauth2/access_token.rb b/lib/oauth2/access_token.rb deleted file mode 100644 index 60f6b090..00000000 --- a/lib/oauth2/access_token.rb +++ /dev/null @@ -1,212 +0,0 @@ -# frozen_string_literal: true - -module OAuth2 - class AccessToken # rubocop:disable Metrics/ClassLength - TOKEN_KEYS_STR = %w[access_token id_token token accessToken idToken].freeze - TOKEN_KEYS_SYM = %i[access_token id_token token accessToken idToken].freeze - TOKEN_KEY_LOOKUP = TOKEN_KEYS_STR + TOKEN_KEYS_SYM - - attr_reader :client, :token, :expires_in, :expires_at, :expires_latency, :params - attr_accessor :options, :refresh_token, :response - - class << self - # Initializes an AccessToken from a Hash - # - # @param [Client] client the OAuth2::Client instance - # @param [Hash] hash a hash of AccessToken property values - # @option hash [String] 'access_token', 'id_token', 'token', :access_token, :id_token, or :token the access token - # @return [AccessToken] the initialized AccessToken - def from_hash(client, hash) - fresh = hash.dup - supported_keys = TOKEN_KEY_LOOKUP & fresh.keys - key = supported_keys[0] - # Having too many is sus, and may lead to bugs. Having none is fine (e.g. refresh flow doesn't need a token). - warn("OAuth2::AccessToken.from_hash: `hash` contained more than one 'token' key (#{supported_keys}); using #{key.inspect}.") if supported_keys.length> 1 - token = fresh.delete(key) - new(client, token, fresh) - end - - # Initializes an AccessToken from a key/value application/x-www-form-urlencoded string - # - # @param [Client] client the OAuth2::Client instance - # @param [String] kvform the application/x-www-form-urlencoded string - # @return [AccessToken] the initialized AccessToken - def from_kvform(client, kvform) - from_hash(client, Rack::Utils.parse_query(kvform)) - end - end - - # Initialize an AccessToken - # - # @param [Client] client the OAuth2::Client instance - # @param [String] token the Access Token value (optional, may not be used in refresh flows) - # @param [Hash] opts the options to create the Access Token with - # @option opts [String] :refresh_token (nil) the refresh_token value - # @option opts [FixNum, String] :expires_in (nil) the number of seconds in which the AccessToken will expire - # @option opts [FixNum, String] :expires_at (nil) the epoch time in seconds in which AccessToken will expire - # @option opts [FixNum, String] :expires_latency (nil) the number of seconds by which AccessToken validity will be reduced to offset latency, @version 2.0+ - # @option opts [Symbol] :mode (:header) the transmission mode of the Access Token parameter value - # one of :header, :body or :query - # @option opts [String] :header_format ('Bearer %s') the string format to use for the Authorization header - # @option opts [String] :param_name ('access_token') the parameter name to use for transmission of the - # Access Token value in :body or :query transmission mode - def initialize(client, token, opts = {}) - @client = client - @token = token.to_s - - opts = opts.dup - %i[refresh_token expires_in expires_at expires_latency].each do |arg| - instance_variable_set("@#{arg}", opts.delete(arg) || opts.delete(arg.to_s)) - end - no_tokens = (@token.nil? || @token.empty?) && (@refresh_token.nil? || @refresh_token.empty?) - if no_tokens - if @client.options[:raise_errors] - error = Error.new(opts) - raise(error) - else - warn('OAuth2::AccessToken has no token') - end - end - @expires_in ||= opts.delete('expires') - @expires_in &&= @expires_in.to_i - @expires_at &&= convert_expires_at(@expires_at) - @expires_latency &&= @expires_latency.to_i - @expires_at ||= Time.now.to_i + @expires_in if @expires_in - @expires_at -= @expires_latency if @expires_latency - @options = {mode: opts.delete(:mode) || :header, - header_format: opts.delete(:header_format) || 'Bearer %s', - param_name: opts.delete(:param_name) || 'access_token'} - @params = opts - end - - # Indexer to additional params present in token response - # - # @param [String] key entry key to Hash - def [](key) - @params[key] - end - - # Whether or not the token expires - # - # @return [Boolean] - def expires? - !!@expires_at - end - - # Whether or not the token is expired - # - # @return [Boolean] - def expired? - expires? && (expires_at <= Time.now.to_i) - end - - # Refreshes the current Access Token - # - # @return [AccessToken] a new AccessToken - # @note options should be carried over to the new AccessToken - def refresh(params = {}, access_token_opts = {}) - raise('A refresh_token is not available') unless refresh_token - - params[:grant_type] = 'refresh_token' - params[:refresh_token] = refresh_token - new_token = @client.get_token(params, access_token_opts) - new_token.options = options - if new_token.refresh_token - # Keep it, if there is one - else - new_token.refresh_token = refresh_token - end - new_token - end - # A compatibility alias - # @note does not modify the receiver, so bang is not the default method - alias refresh! refresh - - # Convert AccessToken to a hash which can be used to rebuild itself with AccessToken.from_hash - # - # @return [Hash] a hash of AccessToken property values - def to_hash - params.merge(access_token: token, refresh_token: refresh_token, expires_at: expires_at) - end - - # Make a request with the Access Token - # - # @param [Symbol] verb the HTTP request method - # @param [String] path the HTTP URL path of the request - # @param [Hash] opts the options to make the request with - # @see Client#request - def request(verb, path, opts = {}, &block) - configure_authentication!(opts) - @client.request(verb, path, opts, &block) - end - - # Make a GET request with the Access Token - # - # @see AccessToken#request - def get(path, opts = {}, &block) - request(:get, path, opts, &block) - end - - # Make a POST request with the Access Token - # - # @see AccessToken#request - def post(path, opts = {}, &block) - request(:post, path, opts, &block) - end - - # Make a PUT request with the Access Token - # - # @see AccessToken#request - def put(path, opts = {}, &block) - request(:put, path, opts, &block) - end - - # Make a PATCH request with the Access Token - # - # @see AccessToken#request - def patch(path, opts = {}, &block) - request(:patch, path, opts, &block) - end - - # Make a DELETE request with the Access Token - # - # @see AccessToken#request - def delete(path, opts = {}, &block) - request(:delete, path, opts, &block) - end - - # Get the headers hash (includes Authorization token) - def headers - {'Authorization' => options[:header_format] % token} - end - - private - - def configure_authentication!(opts) - case options[:mode] - when :header - opts[:headers] ||= {} - opts[:headers].merge!(headers) - when :query - opts[:params] ||= {} - opts[:params][options[:param_name]] = token - when :body - opts[:body] ||= {} - if opts[:body].is_a?(Hash) - opts[:body][options[:param_name]] = token - else - opts[:body] += "&#{options[:param_name]}=#{token}" - end - # @todo support for multi-part (file uploads) - else - raise("invalid :mode option of #{options[:mode]}") - end - end - - def convert_expires_at(expires_at) - Time.iso8601(expires_at.to_s).to_i - rescue ArgumentError - expires_at.to_i - end - end -end diff --git a/lib/oauth2/authenticator.rb b/lib/oauth2/authenticator.rb deleted file mode 100644 index eafc3ef0..00000000 --- a/lib/oauth2/authenticator.rb +++ /dev/null @@ -1,75 +0,0 @@ -# frozen_string_literal: true - -require 'base64' - -module OAuth2 - class Authenticator - attr_reader :mode, :id, :secret - - def initialize(id, secret, mode) - @id = id - @secret = secret - @mode = mode - end - - # Apply the request credentials used to authenticate to the Authorization Server - # - # Depending on configuration, this might be as request params or as an - # Authorization header. - # - # User-provided params and header take precedence. - # - # @param [Hash] params a Hash of params for the token endpoint - # @return [Hash] params amended with appropriate authentication details - def apply(params) - case mode.to_sym - when :basic_auth - apply_basic_auth(params) - when :request_body - apply_params_auth(params) - when :tls_client_auth - apply_client_id(params) - when :private_key_jwt - params - else - raise NotImplementedError - end - end - - def self.encode_basic_auth(user, password) - "Basic #{Base64.strict_encode64("#{user}:#{password}")}" - end - - private - - # Adds client_id and client_secret request parameters if they are not - # already set. - def apply_params_auth(params) - result = {} - result['client_id'] = id unless id.nil? - result['client_secret'] = secret unless secret.nil? - result.merge(params) - end - - # When using schemes that don't require the client_secret to be passed i.e TLS Client Auth, - # we don't want to send the secret - def apply_client_id(params) - result = {} - result['client_id'] = id unless id.nil? - result.merge(params) - end - - # Adds an `Authorization` header with Basic Auth credentials if and only if - # it is not already set in the params. - def apply_basic_auth(params) - headers = params.fetch(:headers, {}) - headers = basic_auth_header.merge(headers) - params.merge(headers: headers) - end - - # @see https://datatracker.ietf.org/doc/html/rfc2617#section-2 - def basic_auth_header - {'Authorization' => self.class.encode_basic_auth(id, secret)} - end - end -end diff --git a/lib/oauth2/client.rb b/lib/oauth2/client.rb deleted file mode 100644 index bffd9cca..00000000 --- a/lib/oauth2/client.rb +++ /dev/null @@ -1,354 +0,0 @@ -# frozen_string_literal: true - -require 'faraday' -require 'logger' - -module OAuth2 - ConnectionError = Class.new(Faraday::ConnectionFailed) - TimeoutError = Class.new(Faraday::TimeoutError) - - # The OAuth2::Client class - class Client # rubocop:disable Metrics/ClassLength - RESERVED_PARAM_KEYS = %w[body headers params parse snaky].freeze - - attr_reader :id, :secret, :site - attr_accessor :options - attr_writer :connection - - # Instantiate a new OAuth 2.0 client using the - # Client ID and Client Secret registered to your - # application. - # - # @param [String] client_id the client_id value - # @param [String] client_secret the client_secret value - # @param [Hash] options the options to create the client with - # @option options [String] :site the OAuth2 provider site host - # @option options [String] :redirect_uri the absolute URI to the Redirection Endpoint for use in authorization grants and token exchange - # @option options [String] :authorize_url ('/oauth/authorize') absolute or relative URL path to the Authorization endpoint - # @option options [String] :token_url ('/oauth/token') absolute or relative URL path to the Token endpoint - # @option options [Symbol] :token_method (:post) HTTP method to use to request token (:get, :post, :post_with_query_string) - # @option options [Symbol] :auth_scheme (:basic_auth) HTTP method to use to authorize request (:basic_auth or :request_body) - # @option options [Hash] :connection_opts ({}) Hash of connection options to pass to initialize Faraday with - # @option options [FixNum] :max_redirects (5) maximum number of redirects to follow - # @option options [Boolean] :raise_errors (true) whether or not to raise an OAuth2::Error on responses with 400+ status codes - # @option options [Logger] :logger (::Logger.new($stdout)) which logger to use when OAUTH_DEBUG is enabled - # @option options [Proc] :extract_access_token proc that takes the client and the response Hash and extracts the access token from the response (DEPRECATED) - # @option options [Class] :access_token_class [Class] class of access token for easier subclassing OAuth2::AccessToken, @version 2.0+ - # @yield [builder] The Faraday connection builder - def initialize(client_id, client_secret, options = {}, &block) - opts = options.dup - @id = client_id - @secret = client_secret - @site = opts.delete(:site) - ssl = opts.delete(:ssl) - warn('OAuth2::Client#initialize argument `extract_access_token` will be removed in oauth2 v3. Refactor to use `access_token_class`.') if opts[:extract_access_token] - @options = { - authorize_url: 'oauth/authorize', - token_url: 'oauth/token', - token_method: :post, - auth_scheme: :basic_auth, - connection_opts: {}, - connection_build: block, - max_redirects: 5, - raise_errors: true, - logger: ::Logger.new($stdout), - access_token_class: AccessToken, - }.merge(opts) - @options[:connection_opts][:ssl] = ssl if ssl - end - - # Set the site host - # - # @param value [String] the OAuth2 provider site host - def site=(value) - @connection = nil - @site = value - end - - # The Faraday connection object - def connection - @connection ||= - Faraday.new(site, options[:connection_opts]) do |builder| - oauth_debug_logging(builder) - if options[:connection_build] - options[:connection_build].call(builder) - else - builder.request :url_encoded # form-encode POST params - builder.adapter Faraday.default_adapter # make requests with Net::HTTP - end - end - end - - # The authorize endpoint URL of the OAuth2 provider - # - # @param [Hash] params additional query parameters - def authorize_url(params = {}) - params = (params || {}).merge(redirection_params) - connection.build_url(options[:authorize_url], params).to_s - end - - # The token endpoint URL of the OAuth2 provider - # - # @param [Hash] params additional query parameters - def token_url(params = nil) - connection.build_url(options[:token_url], params).to_s - end - - # Makes a request relative to the specified site root. - # Updated HTTP 1.1 specification (IETF RFC 7231) relaxed the original constraint (IETF RFC 2616), - # allowing the use of relative URLs in Location headers. - # @see https://datatracker.ietf.org/doc/html/rfc7231#section-7.1.2 - # - # @param [Symbol] verb one of :get, :post, :put, :delete - # @param [String] url URL path of request - # @param [Hash] opts the options to make the request with - # @option opts [Hash] :params additional query parameters for the URL of the request - # @option opts [Hash, String] :body the body of the request - # @option opts [Hash] :headers http request headers - # @option opts [Boolean] :raise_errors whether or not to raise an OAuth2::Error on 400+ status - # code response for this request. Will default to client option - # @option opts [Symbol] :parse @see Response::initialize - # @option opts [true, false] :snaky (true) @see Response::initialize - # @yield [req] @see Faraday::Connection#run_request - def request(verb, url, opts = {}, &block) - response = execute_request(verb, url, opts, &block) - - case response.status - when 301, 302, 303, 307 - opts[:redirect_count] ||= 0 - opts[:redirect_count] += 1 - return response if opts[:redirect_count]> options[:max_redirects] - - if response.status == 303 - verb = :get - opts.delete(:body) - end - location = response.headers['location'] - if location - full_location = response.response.env.url.merge(location) - request(verb, full_location, opts) - else - error = Error.new(response) - raise(error, "Got #{response.status} status code, but no Location header was present") - end - when 200..299, 300..399 - # on non-redirecting 3xx statuses, just return the response - response - when 400..599 - error = Error.new(response) - raise(error) if opts.fetch(:raise_errors, options[:raise_errors]) - - response - else - error = Error.new(response) - raise(error, "Unhandled status code value of #{response.status}") - end - end - - # Initializes an AccessToken by making a request to the token endpoint - # - # @param params [Hash] a Hash of params for the token endpoint, except: - # @option params [Symbol] :parse @see Response#initialize - # @option params [true, false] :snaky (true) @see Response#initialize - # @param access_token_opts [Hash] access token options, to pass to the AccessToken object - # @param extract_access_token [Proc] proc that extracts the access token from the response (DEPRECATED) - # @yield [req] @see Faraday::Connection#run_request - # @return [AccessToken] the initialized AccessToken - def get_token(params, access_token_opts = {}, extract_access_token = nil, &block) - warn('OAuth2::Client#get_token argument `extract_access_token` will be removed in oauth2 v3. Refactor to use `access_token_class` on #initialize.') if extract_access_token - extract_access_token ||= options[:extract_access_token] - parse, snaky, params, headers = parse_snaky_params_headers(params) - - request_opts = { - raise_errors: options[:raise_errors], - parse: parse, - snaky: snaky, - } - if options[:token_method] == :post - - # NOTE: If proliferation of request types continues we should implement a parser solution for Request, - # just like we have with Response. - request_opts[:body] = if headers['Content-Type'] == 'application/json' - params.to_json - else - params - end - - request_opts[:headers] = {'Content-Type' => 'application/x-www-form-urlencoded'} - else - request_opts[:params] = params - request_opts[:headers] = {} - end - request_opts[:headers].merge!(headers) - response = request(http_method, token_url, request_opts, &block) - - # In v1.4.x, the deprecated extract_access_token option retrieves the token from the response. - # We preserve this behavior here, but a custom access_token_class that implements #from_hash - # should be used instead. - if extract_access_token - parse_response_legacy(response, access_token_opts, extract_access_token) - else - parse_response(response, access_token_opts) - end - end - - # The HTTP Method of the request - # @return [Symbol] HTTP verb, one of :get, :post, :put, :delete - def http_method - http_meth = options[:token_method].to_sym - return :post if http_meth == :post_with_query_string - - http_meth - end - - # The Authorization Code strategy - # - # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.1 - def auth_code - @auth_code ||= OAuth2::Strategy::AuthCode.new(self) - end - - # The Implicit strategy - # - # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-26#section-4.2 - def implicit - @implicit ||= OAuth2::Strategy::Implicit.new(self) - end - - # The Resource Owner Password Credentials strategy - # - # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.3 - def password - @password ||= OAuth2::Strategy::Password.new(self) - end - - # The Client Credentials strategy - # - # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.4 - def client_credentials - @client_credentials ||= OAuth2::Strategy::ClientCredentials.new(self) - end - - def assertion - @assertion ||= OAuth2::Strategy::Assertion.new(self) - end - - # The redirect_uri parameters, if configured - # - # The redirect_uri query parameter is OPTIONAL (though encouraged) when - # requesting authorization. If it is provided at authorization time it MUST - # also be provided with the token exchange request. - # - # Providing the :redirect_uri to the OAuth2::Client instantiation will take - # care of managing this. - # - # @api semipublic - # - # @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1 - # @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3 - # @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.2.1 - # @see https://datatracker.ietf.org/doc/html/rfc6749#section-10.6 - # @return [Hash] the params to add to a request or URL - def redirection_params - if options[:redirect_uri] - {'redirect_uri' => options[:redirect_uri]} - else - {} - end - end - - private - - def parse_snaky_params_headers(params) - params = params.map do |key, value| - if RESERVED_PARAM_KEYS.include?(key) - [key.to_sym, value] - else - [key, value] - end - end.to_h - parse = params.key?(:parse) ? params.delete(:parse) : Response::DEFAULT_OPTIONS[:parse] - snaky = params.key?(:snaky) ? params.delete(:snaky) : Response::DEFAULT_OPTIONS[:snaky] - params = authenticator.apply(params) - # authenticator may add :headers, and we remove them here - headers = params.delete(:headers) || {} - [parse, snaky, params, headers] - end - - def execute_request(verb, url, opts = {}) - url = connection.build_url(url).to_s - - begin - response = connection.run_request(verb, url, opts[:body], opts[:headers]) do |req| - req.params.update(opts[:params]) if opts[:params] - yield(req) if block_given? - end - rescue Faraday::ConnectionFailed => e - raise ConnectionError, e - rescue Faraday::TimeoutError => e - raise TimeoutError, e - end - - parse = opts.key?(:parse) ? opts.delete(:parse) : Response::DEFAULT_OPTIONS[:parse] - snaky = opts.key?(:snaky) ? opts.delete(:snaky) : Response::DEFAULT_OPTIONS[:snaky] - - Response.new(response, parse: parse, snaky: snaky) - end - - # Returns the authenticator object - # - # @return [Authenticator] the initialized Authenticator - def authenticator - Authenticator.new(id, secret, options[:auth_scheme]) - end - - def parse_response_legacy(response, access_token_opts, extract_access_token) - access_token = build_access_token_legacy(response, access_token_opts, extract_access_token) - - return access_token if access_token - - if options[:raise_errors] - error = Error.new(response) - raise(error) - end - - nil - end - - def parse_response(response, access_token_opts) - access_token_class = options[:access_token_class] - data = response.parsed - - unless data.is_a?(Hash) && !data.empty? - return unless options[:raise_errors] - - error = Error.new(response) - raise(error) - end - - build_access_token(response, access_token_opts, access_token_class) - end - - # Builds the access token from the response of the HTTP call - # - # @return [AccessToken] the initialized AccessToken - def build_access_token(response, access_token_opts, access_token_class) - access_token_class.from_hash(self, response.parsed.merge(access_token_opts)).tap do |access_token| - access_token.response = response if access_token.respond_to?(:response=) - end - end - - # Builds the access token from the response of the HTTP call with legacy extract_access_token - # - # @return [AccessToken] the initialized AccessToken - def build_access_token_legacy(response, access_token_opts, extract_access_token) - extract_access_token.call(self, response.parsed.merge(access_token_opts)) - rescue StandardError - nil - end - - def oauth_debug_logging(builder) - builder.response :logger, options[:logger], bodies: true if ENV['OAUTH_DEBUG'] == 'true' - end - end -end diff --git a/lib/oauth2/error.rb b/lib/oauth2/error.rb deleted file mode 100644 index cd99ff86..00000000 --- a/lib/oauth2/error.rb +++ /dev/null @@ -1,59 +0,0 @@ -# frozen_string_literal: true - -module OAuth2 - class Error < StandardError - attr_reader :response, :body, :code, :description - - # standard error codes include: - # 'invalid_request', 'invalid_client', 'invalid_token', 'invalid_grant', 'unsupported_grant_type', 'invalid_scope' - # response might be a Response object, or the response.parsed hash - def initialize(response) - @response = response - if response.respond_to?(:parsed) - if response.parsed.is_a?(Hash) - @code = response.parsed['error'] - @description = response.parsed['error_description'] - end - elsif response.is_a?(Hash) - @code = response['error'] - @description = response['error_description'] - end - @body = if response.respond_to?(:body) - response.body - else - @response - end - message_opts = parse_error_description(@code, @description) - super(error_message(@body, message_opts)) - end - - private - - def error_message(response_body, opts = {}) - lines = [] - - lines << opts[:error_description] if opts[:error_description] - - error_string = if response_body.respond_to?(:encode) && opts[:error_description].respond_to?(:encoding) - script_encoding = opts[:error_description].encoding - response_body.encode(script_encoding, invalid: :replace, undef: :replace) - else - response_body - end - - lines << error_string - - lines.join("\n") - end - - def parse_error_description(code, description) - return {} unless code || description - - error_description = '' - error_description += "#{code}: " if code - error_description += description if description - - {error_description: error_description} - end - end -end diff --git a/lib/oauth2/response.rb b/lib/oauth2/response.rb deleted file mode 100644 index b1742e7c..00000000 --- a/lib/oauth2/response.rb +++ /dev/null @@ -1,148 +0,0 @@ -# frozen_string_literal: true - -require 'json' -require 'multi_xml' -require 'rack' - -module OAuth2 - # OAuth2::Response class - class Response - DEFAULT_OPTIONS = { - parse: :automatic, - snaky: true, - }.freeze - attr_reader :response - attr_accessor :options - - # Procs that, when called, will parse a response body according - # to the specified format. - @@parsers = { - query: ->(body) { Rack::Utils.parse_query(body) }, - text: ->(body) { body }, - } - - # Content type assignments for various potential HTTP content types. - @@content_types = { - 'application/x-www-form-urlencoded' => :query, - 'text/plain' => :text, - } - - # Adds a new content type parser. - # - # @param [Symbol] key A descriptive symbol key such as :json or :query. - # @param [Array] mime_types One or more mime types to which this parser applies. - # @yield [String] A block returning parsed content. - def self.register_parser(key, mime_types, &block) - key = key.to_sym - @@parsers[key] = block - Array(mime_types).each do |mime_type| - @@content_types[mime_type] = key - end - end - - # Initializes a Response instance - # - # @param [Faraday::Response] response The Faraday response instance - # @param [Symbol] parse (:automatic) how to parse the response body. one of :query (for x-www-form-urlencoded), - # :json, or :automatic (determined by Content-Type response header) - # @param [true, false] snaky (true) Convert @parsed to a snake-case, - # indifferent-access OAuth2::SnakyHash, which is a subclass of Hashie::Mash::Rash (from rash_alt gem)? - # @param [Hash] options all other options for initializing the instance - def initialize(response, parse: :automatic, snaky: true, **options) - @response = response - @options = { - parse: parse, - snaky: snaky, - }.merge(options) - end - - # The HTTP response headers - def headers - response.headers - end - - # The HTTP response status code - def status - response.status - end - - # The HTTP response body - def body - response.body || '' - end - - # The {#response} {#body} as parsed by {#parser}. - # - # @return [Object] As returned by {#parser} if it is #call-able. - # @return [nil] If the {#parser} is not #call-able. - def parsed - return @parsed if defined?(@parsed) - - @parsed = - if parser.respond_to?(:call) - case parser.arity - when 0 - parser.call - when 1 - parser.call(body) - else - parser.call(body, response) - end - end - - @parsed = OAuth2::SnakyHash.new(@parsed) if options[:snaky] && @parsed.is_a?(Hash) - - @parsed - end - - # Attempts to determine the content type of the response. - def content_type - return nil unless response.headers - - ((response.headers.values_at('content-type', 'Content-Type').compact.first || '').split(';').first || '').strip.downcase - end - - # Determines the parser (a Proc or other Object which responds to #call) - # that will be passed the {#body} (and optional {#response}) to supply - # {#parsed}. - # - # The parser can be supplied as the +:parse+ option in the form of a Proc - # (or other Object responding to #call) or a Symbol. In the latter case, - # the actual parser will be looked up in {@@parsers} by the supplied Symbol. - # - # If no +:parse+ option is supplied, the lookup Symbol will be determined - # by looking up {#content_type} in {@@content_types}. - # - # If {#parser} is a Proc, it will be called with no arguments, just - # {#body}, or {#body} and {#response}, depending on the Proc's arity. - # - # @return [Proc, #call] If a parser was found. - # @return [nil] If no parser was found. - def parser - return @parser if defined?(@parser) - - @parser = - if options[:parse].respond_to?(:call) - options[:parse] - elsif options[:parse] - @@parsers[options[:parse].to_sym] - end - - @parser ||= @@parsers[@@content_types[content_type]] - end - end -end - -OAuth2::Response.register_parser(:xml, ['text/xml', 'application/rss+xml', 'application/rdf+xml', 'application/atom+xml', 'application/xml']) do |body| - next body unless body.respond_to?(:to_str) - - MultiXml.parse(body) -end - -OAuth2::Response.register_parser(:json, ['application/json', 'text/javascript', 'application/hal+json', 'application/vnd.collection+json', 'application/vnd.api+json', 'application/problem+json']) do |body| - next body unless body.respond_to?(:to_str) - - body = body.dup.force_encoding(::Encoding::ASCII_8BIT) if body.respond_to?(:force_encoding) - - ::JSON.parse(body) -end diff --git a/lib/oauth2/snaky_hash.rb b/lib/oauth2/snaky_hash.rb deleted file mode 100644 index 836d9adb..00000000 --- a/lib/oauth2/snaky_hash.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true - -module OAuth2 - # Hash which allow assign string key in camel case - # and query on both camel and snake case - class SnakyHash < ::Hashie::Mash::Rash - end -end diff --git a/lib/oauth2/strategy/assertion.rb b/lib/oauth2/strategy/assertion.rb deleted file mode 100644 index 5d921fbc..00000000 --- a/lib/oauth2/strategy/assertion.rb +++ /dev/null @@ -1,102 +0,0 @@ -# frozen_string_literal: true - -require 'jwt' - -module OAuth2 - module Strategy - # The Client Assertion Strategy - # - # @see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-10#section-4.1.3 - # - # Sample usage: - # client = OAuth2::Client.new(client_id, client_secret, - # :site => 'http://localhost:8080', - # :auth_scheme => :request_body) - # - # claim_set = { - # :iss => "http://localhost:3001", - # :aud => "http://localhost:8080/oauth2/token", - # :sub => "me@example.com", - # :exp => Time.now.utc.to_i + 3600, - # } - # - # encoding = { - # :algorithm => 'HS256', - # :key => 'secret_key', - # } - # - # access = client.assertion.get_token(claim_set, encoding) - # access.token # actual access_token string - # access.get("/api/stuff") # making api calls with access token in header - # - class Assertion < Base - # Not used for this strategy - # - # @raise [NotImplementedError] - def authorize_url - raise(NotImplementedError, 'The authorization endpoint is not used in this strategy') - end - - # Retrieve an access token given the specified client. - # - # @param [Hash] claims the hash representation of the claims that should be encoded as a JWT (JSON Web Token) - # - # For reading on JWT and claim keys: - # @see https://github.com/jwt/ruby-jwt - # @see https://datatracker.ietf.org/doc/html/rfc7519#section-4.1 - # @see https://datatracker.ietf.org/doc/html/rfc7523#section-3 - # @see https://www.iana.org/assignments/jwt/jwt.xhtml - # - # There are many possible claim keys, and applications may ask for their own custom keys. - # Some typically required ones: - # :iss (issuer) - # :aud (audience) - # :sub (subject) -- formerly :prn https://datatracker.ietf.org/doc/html/draft-ietf-oauth-json-web-token-06#appendix-F - # :exp, (expiration time) -- in seconds, e.g. Time.now.utc.to_i + 3600 - # - # Note that this method does *not* validate presence of those four claim keys indicated as required by RFC 7523. - # There are endpoints that may not conform with this RFC, and this gem should still work for those use cases. - # - # @param [Hash] encoding_opts a hash containing instructions on how the JWT should be encoded - # @option algorithm [String] the algorithm with which you would like the JWT to be encoded - # @option key [Object] the key with which you would like to encode the JWT - # - # These two options are passed directly to `JWT.encode`. For supported encoding arguments: - # @see https://github.com/jwt/ruby-jwt#algorithms-and-usage - # @see https://datatracker.ietf.org/doc/html/rfc7518#section-3.1 - # - # The object type of `:key` may depend on the value of `:algorithm`. Sample arguments: - # get_token(claim_set, {:algorithm => 'HS256', :key => 'secret_key'}) - # get_token(claim_set, {:algorithm => 'RS256', :key => OpenSSL::PKCS12.new(File.read('my_key.p12'), 'not_secret')}) - # - # @param [Hash] request_opts options that will be used to assemble the request - # @option request_opts [String] :scope the url parameter `scope` that may be required by some endpoints - # @see https://datatracker.ietf.org/doc/html/rfc7521#section-4.1 - # - # @param [Hash] response_opts this will be merged with the token response to create the AccessToken object - # @see the access_token_opts argument to Client#get_token - - def get_token(claims, encoding_opts, request_opts = {}, response_opts = {}) - assertion = build_assertion(claims, encoding_opts) - params = build_request(assertion, request_opts) - - @client.get_token(params, response_opts) - end - - private - - def build_request(assertion, request_opts = {}) - { - grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', - assertion: assertion, - }.merge(request_opts) - end - - def build_assertion(claims, encoding_opts) - raise ArgumentError.new(message: 'Please provide an encoding_opts hash with :algorithm and :key') if !encoding_opts.is_a?(Hash) || (%i[algorithm key] - encoding_opts.keys).any? - - JWT.encode(claims, encoding_opts[:key], encoding_opts[:algorithm]) - end - end - end -end diff --git a/lib/oauth2/strategy/auth_code.rb b/lib/oauth2/strategy/auth_code.rb deleted file mode 100644 index f3aaad0a..00000000 --- a/lib/oauth2/strategy/auth_code.rb +++ /dev/null @@ -1,47 +0,0 @@ -# frozen_string_literal: true - -module OAuth2 - module Strategy - # The Authorization Code Strategy - # - # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.1 - class AuthCode < Base - # The required query parameters for the authorize URL - # - # @param [Hash] params additional query parameters - def authorize_params(params = {}) - params.merge('response_type' => 'code', 'client_id' => @client.id) - end - - # The authorization URL endpoint of the provider - # - # @param [Hash] params additional query parameters for the URL - def authorize_url(params = {}) - assert_valid_params(params) - @client.authorize_url(authorize_params.merge(params)) - end - - # Retrieve an access token given the specified validation code. - # - # @param [String] code The Authorization Code value - # @param [Hash] params additional params - # @param [Hash] opts access_token_opts, @see Client#get_token - # @note that you must also provide a :redirect_uri with most OAuth 2.0 providers - def get_token(code, params = {}, opts = {}) - params = {'grant_type' => 'authorization_code', 'code' => code}.merge(@client.redirection_params).merge(params) - params_dup = params.dup - params.each_key do |key| - params_dup[key.to_s] = params_dup.delete(key) if key.is_a?(Symbol) - end - - @client.get_token(params_dup, opts) - end - - private - - def assert_valid_params(params) - raise(ArgumentError, 'client_secret is not allowed in authorize URL query params') if params.key?(:client_secret) || params.key?('client_secret') - end - end - end -end diff --git a/lib/oauth2/strategy/base.rb b/lib/oauth2/strategy/base.rb deleted file mode 100644 index 801a723e..00000000 --- a/lib/oauth2/strategy/base.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -module OAuth2 - module Strategy - class Base - def initialize(client) - @client = client - end - end - end -end diff --git a/lib/oauth2/strategy/client_credentials.rb b/lib/oauth2/strategy/client_credentials.rb deleted file mode 100644 index 2fba0e86..00000000 --- a/lib/oauth2/strategy/client_credentials.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -module OAuth2 - module Strategy - # The Client Credentials Strategy - # - # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.4 - class ClientCredentials < Base - # Not used for this strategy - # - # @raise [NotImplementedError] - def authorize_url - raise(NotImplementedError, 'The authorization endpoint is not used in this strategy') - end - - # Retrieve an access token given the specified client. - # - # @param [Hash] params additional params - # @param [Hash] opts options - def get_token(params = {}, opts = {}) - params = params.merge('grant_type' => 'client_credentials') - @client.get_token(params, opts) - end - end - end -end diff --git a/lib/oauth2/strategy/implicit.rb b/lib/oauth2/strategy/implicit.rb deleted file mode 100644 index 5e61d1d6..00000000 --- a/lib/oauth2/strategy/implicit.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -module OAuth2 - module Strategy - # The Implicit Strategy - # - # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-26#section-4.2 - class Implicit < Base - # The required query parameters for the authorize URL - # - # @param [Hash] params additional query parameters - def authorize_params(params = {}) - params.merge('response_type' => 'token', 'client_id' => @client.id) - end - - # The authorization URL endpoint of the provider - # - # @param [Hash] params additional query parameters for the URL - def authorize_url(params = {}) - assert_valid_params(params) - @client.authorize_url(authorize_params.merge(params)) - end - - # Not used for this strategy - # - # @raise [NotImplementedError] - def get_token(*) - raise(NotImplementedError, 'The token is accessed differently in this strategy') - end - - private - - def assert_valid_params(params) - raise(ArgumentError, 'client_secret is not allowed in authorize URL query params') if params.key?(:client_secret) || params.key?('client_secret') - end - end - end -end diff --git a/lib/oauth2/strategy/password.rb b/lib/oauth2/strategy/password.rb deleted file mode 100644 index d41ca07a..00000000 --- a/lib/oauth2/strategy/password.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -module OAuth2 - module Strategy - # The Resource Owner Password Credentials Authorization Strategy - # - # @see http://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-15#section-4.3 - class Password < Base - # Not used for this strategy - # - # @raise [NotImplementedError] - def authorize_url - raise(NotImplementedError, 'The authorization endpoint is not used in this strategy') - end - - # Retrieve an access token given the specified End User username and password. - # - # @param [String] username the End User username - # @param [String] password the End User password - # @param [Hash] params additional params - def get_token(username, password, params = {}, opts = {}) - params = {'grant_type' => 'password', - 'username' => username, - 'password' => password}.merge(params) - @client.get_token(params, opts) - end - end - end -end diff --git a/lib/oauth2/version.rb b/lib/oauth2/version.rb deleted file mode 100644 index 46ec4307..00000000 --- a/lib/oauth2/version.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -module OAuth2 - module Version - VERSION = '2.0.7'.freeze - end -end diff --git a/oauth2.gemspec b/oauth2.gemspec deleted file mode 100644 index 1af5d585..00000000 --- a/oauth2.gemspec +++ /dev/null @@ -1,68 +0,0 @@ -# encoding: utf-8 -# frozen_string_literal: true - -require_relative 'lib/oauth2/version' - -Gem::Specification.new do |spec| - spec.add_dependency 'faraday', ['>= 0.17.3', '< 3.0'] - spec.add_dependency 'jwt', ['>= 1.0', '< 3.0'] - spec.add_dependency 'multi_xml', '~> 0.5' - spec.add_dependency 'rack', ['>= 1.2', '< 3'] - spec.add_dependency 'rash_alt', ['>= 0.4', '< 1'] - spec.add_dependency 'version_gem', '~> 1.1' - - spec.authors = ['Peter Boling', 'Erik Michaels-Ober', 'Michael Bleigh'] - spec.description = 'A Ruby wrapper for the OAuth 2.0 protocol built with a similar style to the original OAuth spec.' - spec.email = ['peter.boling@gmail.com'] - spec.homepage = 'https://github.com/oauth-xx/oauth2' - spec.licenses = %w[MIT] - spec.name = 'oauth2' - spec.required_ruby_version = '>= 2.2.0' - spec.summary = 'A Ruby wrapper for the OAuth 2.0 protocol.' - spec.version = OAuth2::Version::VERSION - spec.post_install_message = " -You have installed oauth2 version #{OAuth2::Version::VERSION}, congratulations! - -There are BREAKING changes, but most will not encounter them, and updating your code should be easy! - -Please see: -• https://github.com/oauth-xx/oauth2#what-is-new-for-v20 -• https://github.com/oauth-xx/oauth2/blob/master/CHANGELOG.md - -Please report issues, and support the project! Thanks, |7eter l-|. l3oling - -" - - spec.metadata['homepage_uri'] = spec.homepage - spec.metadata['source_code_uri'] = "#{spec.homepage}/tree/v#{spec.version}" - spec.metadata['changelog_uri'] = "#{spec.homepage}/blob/v#{spec.version}/CHANGELOG.md" - spec.metadata['bug_tracker_uri'] = "#{spec.homepage}/issues" - spec.metadata['documentation_uri'] = "https://www.rubydoc.info/gems/#{spec.name}/#{spec.version}" - spec.metadata['wiki_uri'] = "#{spec.homepage}/wiki" - spec.metadata['rubygems_mfa_required'] = 'true' - - spec.require_paths = %w[lib] - spec.bindir = 'exe' - spec.files = Dir[ - 'lib/**/*', - 'CHANGELOG.md', - 'CODE_OF_CONDUCT.md', - 'CONTRIBUTING.md', - 'LICENSE', - 'README.md', - 'SECURITY.md', - ] - spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } - - spec.add_development_dependency 'addressable', '>= 2' - spec.add_development_dependency 'backports', '>= 3' - spec.add_development_dependency 'bundler', '>= 2' - spec.add_development_dependency 'rake', '>= 12' - spec.add_development_dependency 'rexml', '>= 3' - spec.add_development_dependency 'rspec', '>= 3' - spec.add_development_dependency 'rspec-block_is_expected' - spec.add_development_dependency 'rspec-pending_for' - spec.add_development_dependency 'rspec-stubbed_env' - spec.add_development_dependency 'rubocop-lts', '~> 8.0' - spec.add_development_dependency 'silent_stream' -end diff --git a/spec/config/faraday.rb b/spec/config/faraday.rb deleted file mode 100644 index 2051ebb1..00000000 --- a/spec/config/faraday.rb +++ /dev/null @@ -1,3 +0,0 @@ -# frozen_string_literal: true - -Faraday.default_adapter = :test diff --git a/spec/config/multi_xml.rb b/spec/config/multi_xml.rb deleted file mode 100644 index 2d788eb5..00000000 --- a/spec/config/multi_xml.rb +++ /dev/null @@ -1,3 +0,0 @@ -# frozen_string_literal: true - -MultiXml.parser = :rexml diff --git a/spec/config/rspec/rspec_core.rb b/spec/config/rspec/rspec_core.rb deleted file mode 100644 index 7ee40059..00000000 --- a/spec/config/rspec/rspec_core.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -RSpec.configure do |config| - # Enable flags like --only-failures and --next-failure - config.example_status_persistence_file_path = '.rspec_status' - - # Disable RSpec exposing methods globally on `Module` and `main` - config.disable_monkey_patching! - - config.expect_with :rspec do |c| - c.syntax = :expect - end -end diff --git a/spec/config/rspec/silent_stream.rb b/spec/config/rspec/silent_stream.rb deleted file mode 100644 index e6755424..00000000 --- a/spec/config/rspec/silent_stream.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -RSpec.configure do |config| - config.include SilentStream -end diff --git a/spec/examples/google_spec.rb b/spec/examples/google_spec.rb deleted file mode 100644 index cf1aa7ee..00000000 --- a/spec/examples/google_spec.rb +++ /dev/null @@ -1,141 +0,0 @@ -# frozen_string_literal: true - -require 'jwt' - -RSpec.describe 'using OAuth2 with Google' do - # This describes authenticating to a Google API via a service account. - # See their docs: https://developers.google.com/identity/protocols/OAuth2ServiceAccount - - describe 'via 2-legged JWT assertion' do - let(:client) do - OAuth2::Client.new( - '', - '', - site: 'https://accounts.google.com', - authorize_url: '/o/oauth2/auth', - token_url: '/o/oauth2/token', - auth_scheme: :request_body - ) - end - - # These are taken directly from Google's documentation example: - - let(:required_claims) do - { - 'iss' => '761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com', - # The email address of the service account. - - 'scope' => 'https://www.googleapis.com/auth/devstorage.readonly https://www.googleapis.com/auth/prediction', - # A space-delimited list of the permissions that the application requests. - - 'aud' => 'https://www.googleapis.com/oauth2/v4/token', - # A descriptor of the intended target of the assertion. When making an access token request this value - # is always https://www.googleapis.com/oauth2/v4/token. - - 'exp' => Time.now.to_i + 3600, - # The expiration time of the assertion, specified as seconds since 00:00:00 UTC, January 1, 1970. This value - # has a maximum of 1 hour after the issued time. - - 'iat' => Time.now.to_i, - # The time the assertion was issued, specified as seconds since 00:00:00 UTC, January 1, 1970. - } - end - - let(:optional_claims) do - { - 'sub' => 'some.user@example.com', - # The email address of the user for which the application is requesting delegated access. - } - end - - let(:algorithm) { 'RS256' } - # Per Google: "Service accounts rely on the RSA SHA-256 algorithm" - - let(:key) do - begin - OpenSSL::PKCS12.new(File.read('spec/fixtures/google_service_account_key.p12'), 'notasecret').key - # This simulates the .p12 file that Google gives you to download and keep somewhere. This is meant to - # illustrate extracting the key and using it to generate the JWT. - rescue OpenSSL::PKCS12::PKCS12Error - # JRuby CI builds are blowing up trying to extract a sample key for some reason. This simulates the end result - # of actually figuring out the problem. - OpenSSL::PKey::RSA.new(1024) - end - end - # Per Google: - - # "Take note of the service account's email address and store the service account's P12 private key file in a - # location accessible to your application. Your application needs them to make authorized API calls." - - let(:encoding_options) { {key: key, algorithm: algorithm} } - - before do - client.connection = Faraday.new(client.site, client.options[:connection_opts]) do |builder| - builder.request :url_encoded - builder.adapter :test do |stub| - stub.post('https://accounts.google.com/o/oauth2/token') do |token_request| - @request_body = Rack::Utils.parse_nested_query(token_request.body).transform_keys(&:to_sym) - - [ - 200, - - { - 'Content-Type' => 'application/json', - }, - - { - 'access_token' => '1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M', - 'token_type' => 'Bearer', - 'expires_in' => 3600, - }.to_json, - ] - end - end - end - end - - context 'when passing the required claims' do - let(:claims) { required_claims } - - it 'sends a JWT with the 5 keys' do - client.assertion.get_token(claims, encoding_options) - - expect(@request_body).not_to be_nil, 'No access token request was made!' - expect(@request_body[:grant_type]).to eq('urn:ietf:params:oauth:grant-type:jwt-bearer') - expect(@request_body[:assertion]).to be_a(String) - - payload, header = JWT.decode(@request_body[:assertion], key, true, algorithm: algorithm) - expect(header['alg']).to eq('RS256') - expect(payload.keys).to match_array(%w[iss scope aud exp iat]) - - # Note that these specifically do _not_ include the 'sub' claim, which is indicated as being 'required' - # by the OAuth2 JWT RFC: https://tools.ietf.org/html/rfc7523#section-3 - # This may indicate that this is a nonstandard use case by Google. - - payload.each do |key, value| - expect(value).to eq(claims[key]) - end - end - end - - context 'when including the optional `sub` claim' do - let(:claims) { required_claims.merge(optional_claims) } - - it 'sends a JWT with the 6 keys' do - client.assertion.get_token(claims, encoding_options) - - expect(@request_body).not_to be_nil, 'No access token request was made!' - expect(@request_body[:grant_type]).to eq('urn:ietf:params:oauth:grant-type:jwt-bearer') - expect(@request_body[:assertion]).to be_a(String) - - payload, header = JWT.decode(@request_body[:assertion], key, true, algorithm: algorithm) - expect(header['alg']).to eq('RS256') - expect(payload.keys).to match_array(%w[iss scope aud exp iat sub]) - - payload.each do |key, value| - expect(value).to eq(claims[key]) - end - end - end - end -end diff --git a/spec/ext/backports.rb b/spec/ext/backports.rb deleted file mode 100644 index 5811858b..00000000 --- a/spec/ext/backports.rb +++ /dev/null @@ -1,3 +0,0 @@ -# frozen_string_literal: true - -require 'backports/2.5.0/hash/transform_keys' diff --git a/spec/fixtures/README.md b/spec/fixtures/README.md deleted file mode 100644 index e8ed536a..00000000 --- a/spec/fixtures/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# RS256 - -## How keys were made - -```shell -# No passphrase -# Generates the public and private keys: -ssh-keygen -t rsa -b 4096 -m PEM -f jwtRS256.key -# Converts the key to PEM format -openssl rsa -in jwtRS256.key -pubout -outform PEM -out jwtRS256.key.pub -``` diff --git a/spec/fixtures/RS256/jwtRS256.key b/spec/fixtures/RS256/jwtRS256.key deleted file mode 100644 index 72005e50..00000000 --- a/spec/fixtures/RS256/jwtRS256.key +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKwIBAAKCAgEA5hdXV/4YSymY1T9VNvK2bWRfulwIty1RnAPNINQmfh3aRRkV -+PNrbC2Crji9G0AHmQwgW1bZ3kgkkpIm6RVn44fHvBvuXkZ9ABgXw0d2cLIHmwOF -xSKmWAm/EW//GszUTLLLsMZUe2udtFJW0jxXB2GRY0WVYuo6Oo58RCeP719lw3Ag -s0YF9/IobxKkGd4BautUPw6ZszAa3o+j0zR74x7ouPxybZAOuPsMxqanyeYJeH4o -sJjLMYV9qem9uG2sj7GENJ8UszcpmGbqxBhexPEB7mgDeONIF0XJF23zdOf8ANE5 -mAU2h2v7M6moAfkdUzJ+j48+VT2omHAzAL5yNcmrl2xiWdyoxOw1Y1UmfEmJYV5V -gGYyZ12JZRKY+szPT+vR+MDuYxbquF40O7kvkFNBfL1yCpzfSQCLnEs4rX8qRzZX -ciLeyq4Ht5FLuRFgxjA//XI8LAmp0u7gk+Q7FUH1UgW3kmJDTG0XaxQxYTBSIO7m -cmyjDyBgKVuQmt5E1ycFeteOVdPD/CG/fPYhthvc4UytEFwsMdNy3iD6/wuUH68t -AKam28UZaOb0qK+00cQQD8fulY9rKtSL10LvJFWUOa/SJyLvk9vUmfvFn182il1n -X6GpyxyMmE/FCnH4CT/DjrSZf08mOO8eL5ofYHMK/oiXr1eODqx+pOwClNsCAwEA -AQKCAgEAy34vMFI4WBk04rx9d/hWoQ7Znu8QgjihaZLvEy6t0HJEfUH/bcqS4fyq -C72Aeh452gCgiUeZrf4t4jdCFHhrBg8q9dHaEiTTHocwVPPZ6zd4hH8sCrpnVYth -IWHkw2YOCLtEbFYrl3AI7Na5lHvrGEsREzQSN4Yh83Has0guAy1iyeNb+FFgq/XO -DtX0ri/rHw1717zo8FIGIXn2EK/lNWw7tIcICKAUdUMK/JGd6XD6RUeGYxDu/CAs -kF55/Sd6Kyd7XjKnUwzhS7kRvlYzUog4BgqVr4+LTZHZlFAYtfcJqAtinXFW1ZQJ -eZp9TSlt5wvMZNjx7t92QUNRyEGmrQAU+8COHnT0/drFf0MCiyHSUN0E7/5fswhc -uMSU9XiJA9G0wYvJl4zIuOuIYWZWhIqvjYSkvdlP70t9XO2gk/ZcCWsMW8i+xbwC -w1+MMjsKsNedXxI99TIPPHcCNMxqlt1E1kHH3SAwCuEH/ez7PRMyEQQ0EyAk22x/ -piYIWXkX5835cLbLRIYafXgOiugWZjCwIqfRIcIpscmcijZwCF2DyevveYdx3krR -FGA2PFydFyxCNG7XwvKb9kHb7WBERUPV/H3eCqu2SZ/RvF+I94LUYP4bu6CmFdO9 -wCJcGJoL1P7tVhS9lA5Oj0QWczrjnejCoI9XMMduWk032rR1VYECggEBAPZDnTBY -H2uiVmGdMfWTAmX86kiHVpkL03OG6rgvDMsMOYKnik9Lb3gNeUIuPeAWFNrXCoD1 -qp0loxPhKSojNOOM8Yiz/GwQ/QI9dzgtxs7E7rFFyTuJcY48Do8uOFyUHbAbeOBF -b9UL/uBfWZGVV1YY753xyqYlCpxTVQGms1jsbVFdZE1iVpOwAkFVuoLYaHLut4zB -01ORyBSoWan173P+IQH6F1uNXE2Kk/FIMDN6bgP1pXkdkrTx4WjAmRnP/Sc4r38/ -F1xN+gxnWGPUKDVRPYBpVzDR036w65ODgg2FROK2vIxlStiAC/rc0JLsvaWfb1Rn -dsWdJJ1V6mZ6a5sCggEBAO8wC1jcIoiBz3xoA8E5BSt8qLJ7ZuSFaaidvWX2/xj6 -lSWJxCGQfhR7P6ozvH6UDo1WbJT6nNyXPkiDkAzcmAdsYVjULW3K2LI9oPajaJxY -L7KJpylgh9JhMvbMz3VVjTgYRt+kjX+3uFMZNx1YfiBP+S6xx5sjK9CKDz3H99kC -q9bX95YFqZ7yFE3aBCR6CENo2tXpMN96CLQGpwa0bwt3xNzC4MhZMXbGR3DdBYbD -tS9lJfQvAVUYxbSE/2FBgjpO6ArMyU2ZUEDFx9J6IhfhVbQV4VeITMyRNo0XwBiQ -/+XpLXgHkw7LiNMIoc7d+M7yLA1Vz7+r8XxWHHZCL8ECggEBAPK8VrYORno7e1Wg -MlxS2WxZzTxMWmlkpLoc5END7SI/HHjSV5wtSORWs40uM0MrwMasa+gNPmzDamjv -6Tllln4ssO8EKe0DGcAZgefYBzxMFNKbbOzIXyvJurga4Ocv/8tUaOL2znJ67nGO -yqSbRYjR724JpKv7mufXo9SK0gD2mhI3MeSs55WPScnIjJzoXpva/QU7D+gxq7vg -7PCAP9RfS329W0Sco7yyuXx8oTY8mTBB8ybcpXzBZmNwY/hzcJ42W5XbRFVxbuTH -APL1beSP/UUTkCPIzuTz0mCGoaxeDjZB1Lu2I/4eyLAu80+/FneoHX5etU23xR1o -UDFOvb0CggEBALTTc6CoPAtLaBs7X6tSelAYHEli9bTKD8kEB83wX4b42ozYjEh7 -vnWpf8Yi+twO/rlnnws6NCCoztNvcxXmJ6FlFGtdbULV2eFWqjwL6ehY2yZ03sVv -Tv+DsE3ZJPYlyW+hGuO0uazWrilUpNAwuJmhHFdq2+azPkqYNVGVvhB37oWsHGd0 -vHmHtkXtDris8VZVDSwu8V3iGnZPmTJ+cn0O/OuRAPM2SyjqWdQ/pA/wIShFpd3n -M3CsG7uP2KokJloCkXaov39E6uEtJRZAc0nudyaAbC4Kw1Tca4tba0SnSm78S/20 -bD8BLN2uZvXH5nQ9rYQfXcIgMZ64UygsfYECggEBAIw0fQaIVmafa0Hz3ipD4PJI -5QNkh2t9hvOCSKm1xYTNATl0q/VIkZoy1WoxY6SSchcObLxQKbJ9ORi4XNr+IJK5 -3C1Qz/3iv/S3/ktgmqGhQiqybkkHZcbqTXB2wxrx+aaLS7PEfYiuYCrPbX93160k -MVns8PjvYU8KCNMbL2e+AiKEt1KkKAZIpNQdeeJOEhV9wuLYFosd400aYssuSOVW -IkJhGI0lT/7FDJaw0LV98DhQtauANPSUQKN5iw6vciwtsaF1kXMfGlMXj58ntiMq -NizQPR6/Ar1ewLPMh1exDoAfLnCIMk8nbSraW+cebLAZctPugUpfpu3j2LM98aE= ------END RSA PRIVATE KEY----- diff --git a/spec/fixtures/RS256/jwtRS256.key.pub b/spec/fixtures/RS256/jwtRS256.key.pub deleted file mode 100644 index 1a2f63d1..00000000 --- a/spec/fixtures/RS256/jwtRS256.key.pub +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5hdXV/4YSymY1T9VNvK2 -bWRfulwIty1RnAPNINQmfh3aRRkV+PNrbC2Crji9G0AHmQwgW1bZ3kgkkpIm6RVn -44fHvBvuXkZ9ABgXw0d2cLIHmwOFxSKmWAm/EW//GszUTLLLsMZUe2udtFJW0jxX -B2GRY0WVYuo6Oo58RCeP719lw3Ags0YF9/IobxKkGd4BautUPw6ZszAa3o+j0zR7 -4x7ouPxybZAOuPsMxqanyeYJeH4osJjLMYV9qem9uG2sj7GENJ8UszcpmGbqxBhe -xPEB7mgDeONIF0XJF23zdOf8ANE5mAU2h2v7M6moAfkdUzJ+j48+VT2omHAzAL5y -Ncmrl2xiWdyoxOw1Y1UmfEmJYV5VgGYyZ12JZRKY+szPT+vR+MDuYxbquF40O7kv -kFNBfL1yCpzfSQCLnEs4rX8qRzZXciLeyq4Ht5FLuRFgxjA//XI8LAmp0u7gk+Q7 -FUH1UgW3kmJDTG0XaxQxYTBSIO7mcmyjDyBgKVuQmt5E1ycFeteOVdPD/CG/fPYh -thvc4UytEFwsMdNy3iD6/wuUH68tAKam28UZaOb0qK+00cQQD8fulY9rKtSL10Lv -JFWUOa/SJyLvk9vUmfvFn182il1nX6GpyxyMmE/FCnH4CT/DjrSZf08mOO8eL5of -YHMK/oiXr1eODqx+pOwClNsCAwEAAQ== ------END PUBLIC KEY----- diff --git a/spec/fixtures/google_service_account_key.p12 b/spec/fixtures/google_service_account_key.p12 deleted file mode 100644 index 8d945088d25eaf189183407ce74c604538b91708..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2994 zcmai0c{tSV8vYG4!x)mKOv)NzFvgHAS)xpKWr;*4G&9yL8Ob(wS+gfwma&U4C`&OZ z%Slv(2-!mzWIw*|oU89Tf1UTap7(m6`?>D>`R}=3G#OsW0AfUw;nGkTG8`Me#Rg&m z<&xq2u^1nd2tj7>Lk<3?1yuu+p(;np!ck(n@c*({swizawjm~_3<*rbc;p*8*wdct zXxu+FE7}aK@s0@t|5i-D_0Zoe`v9Fd&(mo0%i?;5!`!bIH}a+;RhU<<8;k0k%$^x? zB9=3_)FAhwK;eG>{40XJyoE2PR~Q&U;kjs5DD!`&GcZ8_G#?apI~)sgU<890z{fft z*I%5tAR!il$VX=(1d~L_;8`#k444@Q!D}OQWK8dlA$oWyCEPtvU%`u6?8oo^zIe~a z2y0%q6L{^F6CwM}g@c>b9nAxPr zvXuAbNxq*29}6+Q!4zDZa#2At=d8_ZM##a7`?BrXQb~`0F>F>jU<+* zxF?%G7fVgM`-qtGEa7#Zu)?f8B@G_G`r?psky~lz zYTeXV%;S(Z0U!wscXPTZO5L5Ko(9SB@;r z5Q*A|IhiW;Md|ZW`gbT^%OJWJZ(q-OB0*_`^&Q{t@GJQWKMNh*#wY0yL^xW16Yfgp zZEC)Jw)3%^R9|U>e<)@lho3i?fe~bsqs<+ben+-z!*wgcwwv+}dx*u0jh*len3gms zFGv9CP@*PY)GDs<3tdgz6dpar$cdk{lbrj6bsr>3dHlhFB&DFzrQ;jp=YUbUSQdL& z%+GjvAyMjfesyhJ)Rqrptga1+K1 zfIiBy$<#q1_mC<@z zlbHMpyRVq`MW_Hmgl337uX}(m|MnS znM=Flj!CIRw$gAtda0eveGUF#?8D9hKiH{{Bb;Ec1a>jtOOK_~U_$@X+2MT-9a>Hc zHfp>_uww1OAb;6PV*QBf_xj;(E*>0XdYuV_vQHQS6ドル`oy<4uzry5cgq)zvikhlr{l zrR>o!evH*kv0~wiqb-=?pVYe7SD6&$ShyH{po8iyQ1Qz z0XTj1b^^?u-F?W_uf>nI(Q#2vSYpe1lX#omn-6{Lxx;S%T;5vEAw(c|`O9PS$J1dA zVP1locATe_A87+6;Wo>hj-AgPipokl6E!{QqLM=nNqFtmq4(2JkegZSTN)q1ドル^eYX@9qe`uX2`A-s%CmV@~r=vN%1u|EB?G4 zK5gM?$KY+E)VCa@>Dwijzw<@)dz%iq)wh?zlu%%(xldpz8hy8b)_nwcqk&#`pzeax6-3bioj>U zM+9T3yO9d$u+8{$ElpNPmwtmHdER5b$elP6R9$Y&ruLgfZEEukP<_+9mc5xfvna{g zR`g}cV0B`O+&4^{EMkYQoAqrL-R${?uXAFTR%yE@OLd z>?d(Oph7W&8kg0V69Pf5+GAUqLwigncnq`l4+r5|LQuJD>`Mk(geM7(Na9287%`(A zzJ9;?M7=z*Y6Y(nKvsgeQmS2u`DZeIP0OWDPpE|!AlQdgtXaOd_UT-Fq+i9;+~+77 zE51?rT*H{8p~rc)tgec#f2L2qE94R*Z|UC2VWLRh6awwFoV}a0?J2OaUo0`;W||%P z<7xgs%llx=bq1zvjab{wfqcv}#jh-t)|n3op8hg-o(hgg73wnl*;m02oiza`z_vde zr0q$aiET-a;(7#n%zXcw*zv~-LsuQL65TNLG1D-pg+1*N?jFvPvfZ~aNh+XywQjja z9yyfuUU5E4_?`}z4&52iaUMVYaG0io!!J2|5)Nl^?vq_AV+1< zsJu6!=aSdKtF8%W=oC^C^8vg8PW4rL?Ww>i{XQbh_Rbae581*q-++0PiJZeZ@t5-- z*vR>~(TMLt1FLJwr=pL!aj^uQ%Cy?6UEO$%le{$}CiD7=4M`>cH1hRjcB-pMlvrO# zt2G^g%u0(+&Py<+rrq#dikz>bj~SmBsu#7Fg34657E4hJdb6?dn68B_UV(|#l}3xi znpJqHk(vT7&8-OaC~z}YdFo9YwIZJ`8U*K#A8ws)TO*#wPRPtz8bp{T>$RvSkuv?2 zzx-ZkoKO|3q+h6w4;S3^0O=4uvJF}iK!cyTM_n8mS{NIzRp<}bdslxxz|cld|g?sy zl34MkbH(qU^p9bWx^RKE%%A3boPbIn+dzM&IjO~dV z;xl#iKSU%FU;A>-NcwB^2fO{X&Ste)7#@J z4+Sb&7Im~y1rCY>qQ$-~lKxX!{y+Uli>eLGGv;GbVHBkk;}IY=@llEvTlQnQha((@ z-|^ihOU~27`Qzt)V`4mdh!5?Tfg|MTHw0D%S9y(6f6Hm%IP)O;Uo zS18W9$%5qoZQiJTF=0B17N-u@P>NZdV%@tsaHm8;@f=P=26aJ3Q@*-X;6TY%QV^q@ o@(kA-Vi8{E-X998ドルr`7C{JR$;l1b58lG4}E+Z(zg^Y_B~Hw52R?f?J) diff --git a/spec/oauth2/access_token_spec.rb b/spec/oauth2/access_token_spec.rb deleted file mode 100644 index 795e3810..00000000 --- a/spec/oauth2/access_token_spec.rb +++ /dev/null @@ -1,716 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe OAuth2::AccessToken do - subject { described_class.new(client, token) } - - let(:base_options) { {site: 'https://api.example.com'} } - let(:options) { {} } - let(:token) { 'monkey' } - let(:refresh_body) { JSON.dump(access_token: 'refreshed_foo', expires_in: 600, refresh_token: 'refresh_bar') } - let(:client) do - OAuth2::Client.new('abc', 'def', options.merge(base_options)) do |builder| - builder.request :url_encoded - builder.adapter :test do |stub| - VERBS.each do |verb| - stub.send(verb, '/token/header') { |env| [200, {}, env[:request_headers]['Authorization']] } - stub.send(verb, "/token/query?access_token=#{token}") { |env| [200, {}, Addressable::URI.parse(env[:url]).query_values['access_token']] } - stub.send(verb, '/token/query_string') { |env| [200, {}, CGI.unescape(Addressable::URI.parse(env[:url]).query)] } - stub.send(verb, '/token/body') { |env| [200, {}, env[:body]] } - end - stub.post('/oauth/token') { |_env| [200, {'Content-Type' => 'application/json'}, refresh_body] } - end - end - end - - describe '.from_hash' do - subject(:target) { described_class.from_hash(client, hash) } - - let(:hash) do - { - :access_token => token, - :id_token => 'confusing bug here', - :refresh_token => 'foobar', - :expires_at => Time.now.to_i + 200, - 'foo' => 'bar', - } - end - - it 'return a hash equals to the hash used to initialize access token' do - expect(target.to_hash).to eq(hash) - end - - context 'with warning for too many tokens' do - subject(:printed) do - capture(:stderr) do - target - end - end - - it 'warns on STDERR' do - msg = <<-msg.lstrip - OAuth2::AccessToken.from_hash: `hash` contained more than one 'token' key ([:access_token, :id_token]); using :access_token. - MSG - expect(printed).to eq(msg) - end - end - - context 'with keys in a different order to the lookup' do - subject(:printed) do - capture(:stderr) do - target - end - end - - let(:hash) do - { - id_token: 'confusing bug here', - access_token: token, - } - end - - it 'warns on STDERR and selects the correct key' do - msg = <<-msg.lstrip - OAuth2::AccessToken.from_hash: `hash` contained more than one 'token' key ([:access_token, :id_token]); using :access_token. - MSG - expect(printed).to eq(msg) - end - end - end - - describe '#initialize' do - it 'assigns client and token' do - expect(subject.client).to eq(client) - expect(subject.token).to eq(token) - end - - it 'assigns extra params' do - target = described_class.new(client, token, 'foo' => 'bar') - expect(target.params).to include('foo') - expect(target.params['foo']).to eq('bar') - end - - def assert_initialized_token(target) - expect(target.token).to eq(token) - expect(target).to be_expires - expect(target.params.keys).to include('foo') - expect(target.params['foo']).to eq('bar') - end - - it 'initializes with a Hash' do - hash = {:access_token => token, :expires_at => Time.now.to_i + 200, 'foo' => 'bar'} - target = described_class.from_hash(client, hash) - assert_initialized_token(target) - end - - it 'from_hash does not modify opts hash' do - hash = {access_token: token, expires_at: Time.now.to_i} - hash_before = hash.dup - described_class.from_hash(client, hash) - expect(hash).to eq(hash_before) - end - - it 'initializes with a form-urlencoded key/value string' do - kvform = "access_token=#{token}&expires_at=#{Time.now.to_i + 200}&foo=bar" - target = described_class.from_kvform(client, kvform) - assert_initialized_token(target) - end - - context 'with options' do - subject(:target) { described_class.new(client, token, **options) } - - context 'with body mode' do - let(:mode) { :body } - let(:options) { {param_name: 'foo', header_format: 'Bearer %', mode: mode} } - - it 'sets options' do - expect(target.options[:param_name]).to eq('foo') - expect(target.options[:header_format]).to eq('Bearer %') - expect(target.options[:mode]).to eq(mode) - end - end - - context 'with header mode' do - let(:mode) { :header } - let(:options) { {headers: {}, mode: mode} } - - it 'sets options' do - expect(target.options[:headers]).to be_nil - expect(target.options[:mode]).to eq(mode) - end - end - - context 'with query mode' do - let(:mode) { :query } - let(:options) { {params: {}, param_name: 'foo', mode: mode} } - - it 'sets options' do - expect(target.options[:param_name]).to eq('foo') - expect(target.options[:params]).to be_nil - expect(target.options[:mode]).to eq(mode) - end - end - - context 'with invalid mode' do - let(:mode) { :this_is_bad } - let(:options) { {mode: mode} } - - it 'does not raise on initialize' do - block_is_expected.not_to raise_error - end - - context 'with request' do - subject(:request) { target.post('/token/header') } - - it 'raises' do - block_is_expected.to raise_error("invalid :mode option of #{mode}") - end - end - - context 'with client.options[:raise_errors] = true' do - let(:mode) { :this_is_bad } - let(:options) { {mode: mode, raise_errors: true} } - - before do - expect(client.options[:raise_errors]).to be(true) - end - - context 'when there is a token' do - it 'does not raise on initialize' do - block_is_expected.not_to raise_error - end - - context 'with request' do - subject(:request) { target.post('/token/header') } - - it 'raises' do - block_is_expected.to raise_error("invalid :mode option of #{mode}") - end - end - end - - context 'when there is empty token' do - let(:token) { '' } - - it 'raises on initialize' do - block_is_expected.to raise_error(OAuth2::Error, '{:mode=>:this_is_bad, :raise_errors=>true}') - end - end - - context 'when there is nil token' do - let(:token) { nil } - - it 'raises on initialize' do - block_is_expected.to raise_error(OAuth2::Error, '{:mode=>:this_is_bad, :raise_errors=>true}') - end - end - end - end - - context 'with client.options[:raise_errors] = false' do - let(:options) { {raise_errors: false} } - - before do - expect(client.options[:raise_errors]).to be(false) - end - - context 'when there is a token' do - let(:token) { 'hurdygurdy' } - - it 'does not raise on initialize' do - block_is_expected.not_to raise_error - end - - it 'has token' do - expect(target.token).to eq(token) - end - - it 'has no refresh_token' do - expect(target.refresh_token).to be_nil - end - - context 'when there is refresh_token' do - let(:options) { {raise_errors: false, refresh_token: 'zxcv'} } - - it 'does not raise on initialize' do - block_is_expected.not_to raise_error - end - - it 'has token' do - expect(target.token).to eq(token) - end - - it 'has refresh_token' do - expect(target.refresh_token).to eq('zxcv') - end - end - end - - context 'when there is empty token' do - let(:token) { '' } - - context 'when there is no refresh_token' do - it 'does not raise on initialize' do - block_is_expected.not_to raise_error - end - - it 'has no token' do - expect(target.token).to eq('') - end - - it 'has no refresh_token' do - expect(target.refresh_token).to be_nil - end - - context 'with warning for no token' do - subject(:printed) do - capture(:stderr) do - target - end - end - - it 'warns on STDERR' do - msg = <<-msg.lstrip - OAuth2::AccessToken has no token - MSG - expect(printed).to eq(msg) - end - end - end - - context 'when there is refresh_token' do - let(:options) { {raise_errors: false, refresh_token: 'qwer'} } - - it 'does not raise on initialize' do - block_is_expected.not_to raise_error - end - - it 'has no token' do - expect(target.token).to eq('') - end - - it 'has refresh_token' do - expect(target.refresh_token).to eq('qwer') - end - end - end - - context 'when there is nil token' do - let(:token) { nil } - - context 'when there is no refresh_token' do - it 'does not raise on initialize' do - block_is_expected.not_to raise_error - end - - it 'has no token' do - expect(target.token).to eq('') - end - - it 'has no refresh_token' do - expect(target.refresh_token).to be_nil - end - - context 'with warning for no token' do - subject(:printed) do - capture(:stderr) do - target - end - end - - it 'warns on STDERR' do - msg = <<-msg.lstrip - OAuth2::AccessToken has no token - MSG - expect(printed).to eq(msg) - end - end - end - - context 'when there is refresh_token' do - let(:options) { {raise_errors: false, refresh_token: 'asdf'} } - - it 'does not raise on initialize' do - block_is_expected.not_to raise_error - end - - it 'has no token' do - expect(target.token).to eq('') - end - - it 'has refresh_token' do - expect(target.refresh_token).to eq('asdf') - end - end - end - end - - context 'with client.options[:raise_errors] = true' do - let(:options) { {raise_errors: true} } - - before do - expect(client.options[:raise_errors]).to be(true) - end - - context 'when there is a token' do - let(:token) { 'hurdygurdy' } - - it 'does not raise on initialize' do - block_is_expected.not_to raise_error - end - - it 'has token' do - expect(target.token).to eq(token) - end - - it 'has no refresh_token' do - expect(target.refresh_token).to be_nil - end - - context 'when there is refresh_token' do - let(:options) { {raise_errors: true, refresh_token: 'zxcv'} } - - it 'does not raise on initialize' do - block_is_expected.not_to raise_error - end - - it 'has token' do - expect(target.token).to eq(token) - end - - it 'has refresh_token' do - expect(target.refresh_token).to eq('zxcv') - end - end - end - - context 'when there is empty token' do - let(:token) { '' } - - context 'when there is no refresh_token' do - it 'raises on initialize' do - block_is_expected.to raise_error(OAuth2::Error, '{:raise_errors=>true}') - end - end - - context 'when there is refresh_token' do - let(:options) { {raise_errors: true, refresh_token: 'qwer'} } - - it 'does not raise on initialize' do - block_is_expected.not_to raise_error - end - - it 'has no token' do - expect(target.token).to eq('') - end - - it 'has refresh_token' do - expect(target.refresh_token).to eq('qwer') - end - end - end - - context 'when there is nil token' do - let(:token) { nil } - - context 'when there is no refresh_token' do - it 'raises on initialize' do - block_is_expected.to raise_error(OAuth2::Error, '{:raise_errors=>true}') - end - end - - context 'when there is refresh_token' do - let(:options) { {raise_errors: true, refresh_token: 'asdf'} } - - it 'does not raise on initialize' do - block_is_expected.not_to raise_error - end - - it 'has no token' do - expect(target.token).to eq('') - end - - it 'has refresh_token' do - expect(target.refresh_token).to eq('asdf') - end - end - end - end - end - - it 'does not modify opts hash' do - opts = {param_name: 'foo', header_format: 'Bearer %', mode: :body} - opts_before = opts.dup - described_class.new(client, token, opts) - expect(opts).to eq(opts_before) - end - - describe 'expires_at' do - let(:expires_at) { 1_361_396_829 } - let(:hash) do - { - :access_token => token, - :expires_at => expires_at.to_s, - 'foo' => 'bar', - } - end - - it 'initializes with an integer timestamp expires_at' do - target = described_class.from_hash(client, hash.merge(expires_at: expires_at)) - assert_initialized_token(target) - expect(target.expires_at).to eql(expires_at) - end - - it 'initializes with a string timestamp expires_at' do - target = described_class.from_hash(client, hash) - assert_initialized_token(target) - expect(target.expires_at).to eql(expires_at) - end - - it 'initializes with a string time expires_at' do - target = described_class.from_hash(client, hash.merge(expires_at: Time.at(expires_at).iso8601)) - assert_initialized_token(target) - expect(target.expires_at).to eql(expires_at) - end - end - - describe 'expires_latency' do - let(:expires_at) { 1_530_000_000 } - let(:expires_in) { 100 } - let(:expires_latency) { 10 } - let(:hash) do - { - access_token: token, - expires_latency: expires_latency, - expires_in: expires_in, - } - end - - it 'sets it via options' do - target = described_class.from_hash(client, hash.merge(expires_latency: expires_latency.to_s)) - expect(target.expires_latency).to eq expires_latency - end - - it 'sets it nil by default' do - hash.delete(:expires_latency) - target = described_class.from_hash(client, hash) - expect(target.expires_latency).to be_nil - end - - it 'reduces expires_at by the given amount' do - allow(Time).to receive(:now).and_return(expires_at) - target = described_class.from_hash(client, hash) - expect(target.expires_at).to eq(expires_at + expires_in - expires_latency) - end - - it 'reduces expires_at by the given amount if expires_at is provided as option' do - target = described_class.from_hash(client, hash.merge(expires_at: expires_at)) - expect(target.expires_at).to eq(expires_at - expires_latency) - end - end - end - - describe '#request' do - context 'with :mode => :header' do - before do - subject.options[:mode] = :header - end - - VERBS.each do |verb| - it "sends the token in the Authorization header for a #{verb.to_s.upcase} request" do - expect(subject.post('/token/header').body).to include(token) - end - end - end - - context 'with :mode => :query' do - before do - subject.options[:mode] = :query - end - - VERBS.each do |verb| - it "sends the token in the body for a #{verb.to_s.upcase} request" do - expect(subject.post('/token/query').body).to eq(token) - end - - it "sends a #{verb.to_s.upcase} request and options[:param_name] include [number]." do - subject.options[:param_name] = 'auth[1]' - expect(subject.__send__(verb, '/token/query_string').body).to include("auth[1]=#{token}") - end - end - end - - context 'with :mode => :body' do - before do - subject.options[:mode] = :body - end - - VERBS.each do |verb| - it "sends the token in the body for a #{verb.to_s.upcase} request" do - expect(subject.post('/token/body').body.split('=').last).to eq(token) - end - - context 'when options[:param_name] include [number]' do - it "sends a #{verb.to_s.upcase} request when body is a hash" do - subject.options[:param_name] = 'auth[1]' - expect(subject.__send__(verb, '/token/body', body: {hi: 'there'}).body).to include("auth%5B1%5D=#{token}") - end - - it "sends a #{verb.to_s.upcase} request when body is overridden as string" do - subject.options[:param_name] = 'snoo[1]' - expect(subject.__send__(verb, '/token/body', body: 'hi_there').body).to include("hi_there&snoo[1]=#{token}") - end - end - end - end - - context 'params include [number]' do - VERBS.each do |verb| - it "sends #{verb.to_s.upcase} correct query" do - expect(subject.__send__(verb, '/token/query_string', params: {'foo[bar][1]' => 'val'}).body).to include('foo[bar][1]=val') - end - end - end - end - - describe '#expires?' do - it 'is false if there is no expires_at' do - expect(described_class.new(client, token)).not_to be_expires - end - - it 'is true if there is an expires_in' do - expect(described_class.new(client, token, refresh_token: 'abaca', expires_in: 600)).to be_expires - end - - it 'is true if there is an expires_at' do - expect(described_class.new(client, token, refresh_token: 'abaca', expires_in: Time.now.getutc.to_i + 600)).to be_expires - end - end - - describe '#expired?' do - it 'is false if there is no expires_in or expires_at' do - expect(described_class.new(client, token)).not_to be_expired - end - - it 'is false if expires_in is in the future' do - expect(described_class.new(client, token, refresh_token: 'abaca', expires_in: 10_800)).not_to be_expired - end - - it 'is true if expires_at is in the past' do - access = described_class.new(client, token, refresh_token: 'abaca', expires_in: 600) - @now = Time.now + 10_800 - allow(Time).to receive(:now).and_return(@now) - expect(access).to be_expired - end - - it 'is true if expires_at is now' do - @now = Time.now - access = described_class.new(client, token, refresh_token: 'abaca', expires_at: @now.to_i) - allow(Time).to receive(:now).and_return(@now) - expect(access).to be_expired - end - end - - describe '#refresh' do - let(:options) { {access_token_class: access_token_class} } - let(:access_token_class) { NewAccessToken } - let(:access) do - described_class.new(client, token, refresh_token: 'abaca', - expires_in: 600, - param_name: 'o_param', - access_token_class: access_token_class) - end - let(:new_access) do - NewAccessToken.new(client, token, refresh_token: 'abaca') - end - - before do - custom_class = Class.new(described_class) do - def self.from_hash(client, hash) - new(client, hash.delete('access_token'), hash) - end - - def self.contains_token?(hash) - hash.key?('refresh_token') - end - end - - stub_const('NewAccessToken', custom_class) - end - - context 'without refresh_token' do - subject(:no_refresh) { no_access.refresh } - - let(:no_access) do - described_class.new(client, token, refresh_token: nil, - expires_in: 600, - param_name: 'o_param', - access_token_class: access_token_class) - end - - it 'raises when no refresh_token' do - block_is_expected.to raise_error('A refresh_token is not available') - end - end - - it 'returns a refresh token with appropriate values carried over' do - refreshed = access.refresh - expect(access.client).to eq(refreshed.client) - expect(access.options[:param_name]).to eq(refreshed.options[:param_name]) - end - - it 'returns a refresh token of the same access token class' do - refreshed = new_access.refresh! - expect(new_access.class).to eq(refreshed.class) - end - - context 'with a nil refresh_token in the response' do - let(:refresh_body) { JSON.dump(access_token: 'refreshed_foo', expires_in: 600, refresh_token: nil) } - - it 'copies the refresh_token from the original token' do - refreshed = access.refresh - - expect(refreshed.refresh_token).to eq(access.refresh_token) - end - end - - context 'with a not-nil refresh_token in the response' do - let(:refresh_body) { JSON.dump(access_token: 'refreshed_foo', expires_in: 600, refresh_token: 'qerwer') } - - it 'copies the refresh_token from the original token' do - refreshed = access.refresh - - expect(refreshed.token).to eq('refreshed_foo') - expect(refreshed.refresh_token).to eq('qerwer') - end - end - - context 'with a not-nil, not camel case, refresh_token in the response' do - let(:refresh_body) { JSON.dump(accessToken: 'refreshed_foo', expires_in: 600, refreshToken: 'qerwer') } - - it 'copies the refresh_token from the original token' do - refreshed = access.refresh - - expect(refreshed.token).to eq('refreshed_foo') - expect(refreshed.refresh_token).to eq('qerwer') - end - end - - context 'with a custom access_token_class' do - let(:access_token_class) { NewAccessToken } - - it 'returns a refresh token of NewAccessToken' do - refreshed = access.refresh! - - expect(new_access.class).to eq(refreshed.class) - end - end - end - - describe '#to_hash' do - it 'return a hash equals to the hash used to initialize access token' do - hash = {:access_token => token, :refresh_token => 'foobar', :expires_at => Time.now.to_i + 200, 'foo' => 'bar'} - access_token = described_class.from_hash(client, hash.clone) - expect(access_token.to_hash).to eq(hash) - end - end -end diff --git a/spec/oauth2/authenticator_spec.rb b/spec/oauth2/authenticator_spec.rb deleted file mode 100644 index 4f06d306..00000000 --- a/spec/oauth2/authenticator_spec.rb +++ /dev/null @@ -1,126 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe OAuth2::Authenticator do - subject do - described_class.new(client_id, client_secret, mode) - end - - let(:client_id) { 'foo' } - let(:client_secret) { 'bar' } - let(:mode) { :undefined } - - it 'raises NotImplementedError for unknown authentication mode' do - expect { subject.apply({}) }.to raise_error(NotImplementedError) - end - - describe '#apply' do - context 'with parameter-based authentication' do - let(:mode) { :request_body } - - it 'adds client_id and client_secret to params' do - output = subject.apply({}) - expect(output).to eq('client_id' => 'foo', 'client_secret' => 'bar') - end - - context 'when client_id nil' do - let(:client_id) { nil } - - it 'ignores client_id, but adds client_secret to params' do - output = subject.apply({}) - expect(output).to eq('client_secret' => 'bar') - end - end - - it 'does not overwrite existing credentials' do - input = {'client_secret' => 's3cr3t'} - output = subject.apply(input) - expect(output).to eq('client_id' => 'foo', 'client_secret' => 's3cr3t') - end - - it 'preserves other parameters' do - input = {'state' => '42', :headers => {'A' => 'b'}} - output = subject.apply(input) - expect(output).to eq( - 'client_id' => 'foo', - 'client_secret' => 'bar', - 'state' => '42', - :headers => {'A' => 'b'} - ) - end - - context 'passing nil secret' do - let(:client_secret) { nil } - - it 'does not set nil client_secret' do - output = subject.apply({}) - expect(output).to eq('client_id' => 'foo') - end - end - - context 'using tls client authentication' do - let(:mode) { :tls_client_auth } - - it 'does not add client_secret' do - output = subject.apply({}) - expect(output).to eq('client_id' => 'foo') - end - end - - context 'using private key jwt authentication' do - let(:mode) { :private_key_jwt } - - it 'does not include client_id or client_secret' do - output = subject.apply({}) - expect(output).to eq({}) - end - end - end - - context 'using tls_client_auth' do - let(:mode) { :tls_client_auth } - - context 'when client_id present' do - let(:client_id) { 'foobar' } - - it 'adds client_id to params' do - output = subject.apply({}) - expect(output).to eq('client_id' => 'foobar') - end - end - - context 'when client_id nil' do - let(:client_id) { nil } - - it 'ignores client_id for params' do - output = subject.apply({}) - expect(output).to eq({}) - end - end - end - - context 'with Basic authentication' do - let(:mode) { :basic_auth } - let(:header) { "Basic #{Base64.strict_encode64("#{client_id}:#{client_secret}")}" } - - it 'encodes credentials in headers' do - output = subject.apply({}) - expect(output).to eq(headers: {'Authorization' => header}) - end - - it 'does not overwrite existing credentials' do - input = {headers: {'Authorization' => 'Bearer abc123'}} - output = subject.apply(input) - expect(output).to eq(headers: {'Authorization' => 'Bearer abc123'}) - end - - it 'does not overwrite existing params or headers' do - input = {'state' => '42', :headers => {'A' => 'b'}} - output = subject.apply(input) - expect(output).to eq( - 'state' => '42', - :headers => {'A' => 'b', 'Authorization' => header} - ) - end - end - end -end diff --git a/spec/oauth2/client_spec.rb b/spec/oauth2/client_spec.rb deleted file mode 100644 index 57539b1b..00000000 --- a/spec/oauth2/client_spec.rb +++ /dev/null @@ -1,948 +0,0 @@ -# coding: utf-8 -# frozen_string_literal: true - -require 'nkf' - -RSpec.describe OAuth2::Client do - subject(:instance) do - described_class.new('abc', 'def', {site: 'https://api.example.com'}.merge(options)) do |builder| - builder.adapter :test do |stub| - stub.get('/success') { |_env| [200, {'Content-Type' => 'text/awesome'}, 'yay'] } - stub.get('/reflect') { |env| [200, {}, env[:body]] } - stub.post('/reflect') { |env| [200, {}, env[:body]] } - stub.get('/unauthorized') { |_env| [401, {'Content-Type' => 'application/json'}, JSON.dump(error: error_value, error_description: error_description_value)] } - stub.get('/conflict') { |_env| [409, {'Content-Type' => 'text/plain'}, 'not authorized'] } - stub.get('/redirect') { |_env| [302, {'Content-Type' => 'text/plain', 'location' => '/success'}, ''] } - stub.get('/redirect_no_loc') { |_env| [302, {'Content-Type' => 'text/plain'}, ''] } - stub.post('/redirect') { |_env| [303, {'Content-Type' => 'text/plain', 'location' => '/reflect'}, ''] } - stub.get('/error') { |_env| [500, {'Content-Type' => 'text/plain'}, 'unknown error'] } - stub.get('/empty_get') { |_env| [204, {}, nil] } - stub.get('/different_encoding') { |_env| [500, {'Content-Type' => 'application/json'}, NKF.nkf('-We', JSON.dump(error: error_value, error_description: '∞'))] } - stub.get('/ascii_8bit_encoding') { |_env| [500, {'Content-Type' => 'application/json'}, JSON.dump(error: 'invalid_request', error_description: 'é').force_encoding('ASCII-8BIT')] } - stub.get('/unhandled_status') { |_env| [600, {}, nil] } - end - end - end - - let!(:error_value) { 'invalid_token' } - let!(:error_description_value) { 'bad bad token' } - let(:options) { {} } - - describe '#initialize' do - it 'assigns id and secret' do - expect(subject.id).to eq('abc') - expect(subject.secret).to eq('def') - end - - it 'assigns site from the options hash' do - expect(subject.site).to eq('https://api.example.com') - end - - it 'assigns Faraday::Connection#host' do - expect(subject.connection.host).to eq('api.example.com') - end - - it 'leaves Faraday::Connection#ssl unset' do - expect(subject.connection.ssl).to be_empty - end - - it 'is able to pass a block to configure the connection' do - builder = double('builder') - - allow(Faraday).to receive(:new).and_yield(builder) - allow(builder).to receive(:response) - - expect(builder).to receive(:adapter).with(:test) - - described_class.new('abc', 'def') do |client| - client.adapter :test - end.connection - end - - it 'defaults raise_errors to true' do - expect(subject.options[:raise_errors]).to be true - end - - it 'allows true/false for raise_errors option' do - client = described_class.new('abc', 'def', site: 'https://api.example.com', raise_errors: false) - expect(client.options[:raise_errors]).to be false - client = described_class.new('abc', 'def', site: 'https://api.example.com', raise_errors: true) - expect(client.options[:raise_errors]).to be true - end - - it 'allows override of raise_errors option' do - client = described_class.new('abc', 'def', site: 'https://api.example.com', raise_errors: true) do |builder| - builder.adapter :test do |stub| - stub.get('/notfound') { |_env| [404, {}, nil] } - end - end - expect(client.options[:raise_errors]).to be true - expect { client.request(:get, '/notfound') }.to raise_error(OAuth2::Error) - response = client.request(:get, '/notfound', raise_errors: false) - expect(response.status).to eq(404) - end - - it 'allows get/post for access_token_method option' do - client = described_class.new('abc', 'def', site: 'https://api.example.com', access_token_method: :get) - expect(client.options[:access_token_method]).to eq(:get) - client = described_class.new('abc', 'def', site: 'https://api.example.com', access_token_method: :post) - expect(client.options[:access_token_method]).to eq(:post) - end - - it 'does not mutate the opts hash argument' do - opts = {site: 'http://example.com/'} - opts2 = opts.dup - described_class.new 'abc', 'def', opts - expect(opts).to eq(opts2) - end - end - - describe '#site=(val)' do - subject(:site) { instance.site = new_site } - - let(:options) do - {site: 'https://example.com/blog'} - end - let(:new_site) { 'https://example.com/sharpie' } - - it 'sets site' do - block_is_expected.to change(instance, :site).from('https://example.com/blog').to('https://example.com/sharpie') - end - - context 'with connection' do - before do - instance.connection - end - - it 'allows connection to reset with new url prefix' do - block_is_expected.to change { instance.connection.url_prefix }.from(URI('https://example.com/blog')).to(URI('https://example.com/sharpie')) - end - end - end - - %w[authorize token].each do |url_type| - describe ":#{url_type}_url option" do - it "defaults to a path of /oauth/#{url_type}" do - expect(subject.send("#{url_type}_url")).to eq("https://api.example.com/oauth/#{url_type}") - end - - it "is settable via the :#{url_type}_url option" do - subject.options[:"#{url_type}_url"] = '/oauth/custom' - expect(subject.send("#{url_type}_url")).to eq('https://api.example.com/oauth/custom') - end - - it 'allows a different host than the site' do - subject.options[:"#{url_type}_url"] = 'https://api.foo.com/oauth/custom' - expect(subject.send("#{url_type}_url")).to eq('https://api.foo.com/oauth/custom') - end - - context 'when a URL with path is used in the site' do - let(:options) do - {site: 'https://example.com/blog'} - end - - it 'generates an authorization URL relative to the site' do - expect(subject.send("#{url_type}_url")).to eq("https://example.com/blog/oauth/#{url_type}") - end - end - end - end - - describe ':redirect_uri option' do - let(:auth_code_params) do - { - 'client_id' => 'abc', - 'client_secret' => 'def', - 'code' => 'code', - 'grant_type' => 'authorization_code', - } - end - - context 'when blank' do - it 'there is no redirect_uri param added to authorization URL' do - expect(subject.authorize_url('a' => 'b')).to eq('https://api.example.com/oauth/authorize?a=b') - end - - it 'does not add the redirect_uri param to the auth_code token exchange request' do - client = described_class.new('abc', 'def', site: 'https://api.example.com', auth_scheme: :request_body) do |builder| - builder.adapter :test do |stub| - stub.post('/oauth/token', auth_code_params) do - [200, {'Content-Type' => 'application/json'}, '{"access_token":"token"}'] - end - end - end - client.auth_code.get_token('code') - end - end - - context 'when set' do - before { subject.options[:redirect_uri] = 'https://site.com/oauth/callback' } - - it 'adds the redirect_uri param to authorization URL' do - expect(subject.authorize_url('a' => 'b')).to eq('https://api.example.com/oauth/authorize?a=b&redirect_uri=https%3A%2F%2Fsite.com%2Foauth%2Fcallback') - end - - it 'adds the redirect_uri param to the auth_code token exchange request' do - client = described_class.new('abc', 'def', redirect_uri: 'https://site.com/oauth/callback', site: 'https://api.example.com', auth_scheme: :request_body) do |builder| - builder.adapter :test do |stub| - stub.post('/oauth/token', auth_code_params.merge('redirect_uri' => 'https://site.com/oauth/callback')) do - [200, {'Content-Type' => 'application/json'}, '{"access_token":"token"}'] - end - end - end - client.auth_code.get_token('code') - end - end - - describe 'custom headers' do - context 'string key headers' do - it 'adds the custom headers to request' do - client = described_class.new('abc', 'def', site: 'https://api.example.com', auth_scheme: :request_body) do |builder| - builder.adapter :test do |stub| - stub.post('/oauth/token') do |env| - expect(env.request_headers).to include('CustomHeader' => 'CustomHeader') - [200, {'Content-Type' => 'application/json'}, '{"access_token":"token"}'] - end - end - end - header_params = {'headers' => {'CustomHeader' => 'CustomHeader'}} - client.auth_code.get_token('code', header_params) - end - end - - context 'symbol key headers' do - it 'adds the custom headers to request' do - client = described_class.new('abc', 'def', site: 'https://api.example.com', auth_scheme: :request_body) do |builder| - builder.adapter :test do |stub| - stub.post('/oauth/token') do |env| - expect(env.request_headers).to include('CustomHeader' => 'CustomHeader') - [200, {'Content-Type' => 'application/json'}, '{"access_token":"token"}'] - end - end - end - header_params = {headers: {'CustomHeader' => 'CustomHeader'}} - client.auth_code.get_token('code', header_params) - end - end - - context 'string key custom headers with basic auth' do - it 'adds the custom headers to request' do - client = described_class.new('abc', 'def', site: 'https://api.example.com') do |builder| - builder.adapter :test do |stub| - stub.post('/oauth/token') do |env| - expect(env.request_headers).to include('CustomHeader' => 'CustomHeader') - [200, {'Content-Type' => 'application/json'}, '{"access_token":"token"}'] - end - end - end - header_params = {'headers' => {'CustomHeader' => 'CustomHeader'}} - client.auth_code.get_token('code', header_params) - end - end - - context 'symbol key custom headers with basic auth' do - it 'adds the custom headers to request' do - client = described_class.new('abc', 'def', site: 'https://api.example.com') do |builder| - builder.adapter :test do |stub| - stub.post('/oauth/token') do |env| - expect(env.request_headers).to include('CustomHeader' => 'CustomHeader') - [200, {'Content-Type' => 'application/json'}, '{"access_token":"token"}'] - end - end - end - header_params = {headers: {'CustomHeader' => 'CustomHeader'}} - client.auth_code.get_token('code', header_params) - end - end - end - end - - describe '#connection' do - context 'when debugging' do - include_context 'with stubbed env' - before do - stub_env('OAUTH_DEBUG' => debug_value) - end - - context 'when OAUTH_DEBUG=true' do - let(:debug_value) { 'true' } - - it 'smoothly handles successive requests' do - silence_all do - # first request (always goes smoothly) - subject.request(:get, '/success') - end - - expect do - # second request (used to throw Faraday::RackBuilder::StackLocked) - subject.request(:get, '/success') - end.not_to raise_error - end - - it 'prints both request and response bodies to STDOUT' do - printed = capture(:stdout) do - subject.request(:get, '/success') - subject.request(:get, '/reflect', body: 'this is magical') - end - expect(printed).to match 'request: GET https://api.example.com/success' - expect(printed).to match 'response: Content-Type:' - expect(printed).to match 'response: yay' - expect(printed).to match 'request: this is magical' - expect(printed).to match 'response: this is magical' - end - end - - context 'when OAUTH_DEBUG=false' do - let(:debug_value) { 'false' } - - it 'smoothly handles successive requests' do - silence_all do - # first request (always goes smoothly) - subject.request(:get, '/success') - end - - expect do - # second request (used to throw Faraday::RackBuilder::StackLocked) - subject.request(:get, '/success') - end.not_to raise_error - end - - it 'prints nothing to STDOUT' do - printed = capture(:stdout) do - subject.request(:get, '/success') - subject.request(:get, '/reflect', body: 'this is magical') - end - expect(printed).to eq '' - end - end - end - end - - describe '#request' do - it 'works with a null response body' do - expect(subject.request(:get, 'empty_get').body).to eq('') - end - - it 'returns on a successful response' do - response = subject.request(:get, '/success') - expect(response.body).to eq('yay') - expect(response.status).to eq(200) - expect(response.headers).to eq('Content-Type' => 'text/awesome') - end - - context 'with ENV' do - include_context 'with stubbed env' - context 'when OAUTH_DEBUG=true' do - before do - stub_env('OAUTH_DEBUG' => 'true') - end - - it 'outputs to $stdout when OAUTH_DEBUG=true' do - output = capture(:stdout) do - subject.request(:get, '/success') - end - logs = [ - 'request: GET https://api.example.com/success', - 'response: Status 200', - 'response: Content-Type: "text/awesome"', - ] - expect(output).to include(*logs) - end - end - end - - it 'posts a body' do - response = subject.request(:post, '/reflect', body: 'foo=bar') - expect(response.body).to eq('foo=bar') - end - - it 'follows redirects properly' do - response = subject.request(:get, '/redirect') - expect(response.body).to eq('yay') - expect(response.status).to eq(200) - expect(response.headers).to eq('Content-Type' => 'text/awesome') - expect(response.response.env.url.to_s).to eq('https://api.example.com/success') - end - - it 'redirects using GET on a 303' do - response = subject.request(:post, '/redirect', body: 'foo=bar') - expect(response.body).to be_empty - expect(response.status).to eq(200) - expect(response.response.env.url.to_s).to eq('https://api.example.com/reflect') - end - - it 'raises an error if a redirect has no Location header' do - expect { subject.request(:get, '/redirect_no_loc') }.to raise_error(OAuth2::Error, 'Got 302 status code, but no Location header was present') - end - - it 'obeys the :max_redirects option' do - max_redirects = subject.options[:max_redirects] - subject.options[:max_redirects] = 0 - response = subject.request(:get, '/redirect') - expect(response.status).to eq(302) - subject.options[:max_redirects] = max_redirects - end - - it 'returns if raise_errors is false' do - subject.options[:raise_errors] = false - response = subject.request(:get, '/unauthorized') - - expect(response.status).to eq(401) - expect(response.headers).to eq('Content-Type' => 'application/json') - end - - %w[/unauthorized /conflict /error /different_encoding /ascii_8bit_encoding].each do |error_path| - it "raises OAuth2::Error on error response to path #{error_path}" do - pending_for(engine: 'jruby', reason: 'https://github.com/jruby/jruby/issues/4921') if error_path == '/different_encoding' - expect { subject.request(:get, error_path) }.to raise_error(OAuth2::Error) - end - end - - it 're-encodes response body in the error message' do - expect { subject.request(:get, '/ascii_8bit_encoding') }.to raise_error do |ex| - expect(ex.message).to eq("invalid_request: é\n{\"error\":\"invalid_request\",\"error_description\":\"��\"}") - expect(ex.message.encoding.name).to eq('UTF-8') - end - end - - it 'parses OAuth2 standard error response' do - expect { subject.request(:get, '/unauthorized') }.to raise_error do |ex| - expect(ex.code).to eq(error_value) - expect(ex.description).to eq(error_description_value) - expect(ex.to_s).to match(/#{error_value}/) - expect(ex.to_s).to match(/#{error_description_value}/) - end - end - - it 'provides the response in the Exception' do - expect { subject.request(:get, '/error') }.to raise_error do |ex| - expect(ex.response).not_to be_nil - expect(ex.to_s).to match(/unknown error/) - end - end - - it 'informs about unhandled status code' do - expect { subject.request(:get, '/unhandled_status') }.to raise_error do |ex| - expect(ex.response).not_to be_nil - expect(ex.to_s).to match(/Unhandled status code value of 600/) - end - end - - context 'when errors are raised by Faraday' do - let(:connection) { instance_double(Faraday::Connection, build_url: double) } - - before do - allow(connection).to( - receive(:run_request).and_raise(faraday_exception) - ) - allow(subject).to receive(:connection).and_return(connection) # rubocop:disable RSpec/SubjectStub - end - - shared_examples 'failed connection handler' do - it 'rescues the exception' do - expect { subject.request(:get, '/redirect') }.to raise_error do |e| - expect(e.class).to eq(expected_exception) - expect(e.message).to eq(faraday_exception.message) - end - end - end - - context 'with Faraday::ConnectionFailed' do - let(:faraday_exception) { Faraday::ConnectionFailed.new('fail') } - let(:expected_exception) { OAuth2::ConnectionError } - - it_behaves_like 'failed connection handler' - end - - context 'with Faraday::TimeoutError' do - let(:faraday_exception) { Faraday::TimeoutError.new('timeout') } - let(:expected_exception) { OAuth2::TimeoutError } - - it_behaves_like 'failed connection handler' - end - end - end - - describe '#get_token' do - it 'returns a configured AccessToken' do - client = stubbed_client do |stub| - stub.post('/oauth/token') do - [200, {'Content-Type' => 'application/json'}, JSON.dump('access_token' => 'the-token')] - end - end - - token = client.get_token({}) - expect(token).to be_a OAuth2::AccessToken - expect(token.token).to eq('the-token') - end - - context 'when parse: :automatic' do - it 'returns a configured AccessToken' do - client = stubbed_client do |stub| - stub.post('/oauth/token') do - [200, {'Content-Type' => 'application/json'}, JSON.dump('access_token' => 'the-token')] - end - end - - token = client.get_token(parse: :automatic) - expect(token).to be_a OAuth2::AccessToken - expect(token.token).to eq('the-token') - end - end - - context 'when parse: :xml but response is JSON' do - it 'returns a configured AccessToken' do - client = stubbed_client do |stub| - stub.post('/oauth/token') do - [200, {'Content-Type' => 'application/json'}, JSON.dump('access_token' => 'the-token')] - end - end - - expect { client.get_token(parse: :xml) }.to raise_error( - MultiXml::ParseError, - 'The document "{\"access_token\":\"the-token\"}" does not have a valid root' - ) - end - end - - context 'when parse is fuzzed' do - it 'returns a configured AccessToken' do - client = stubbed_client do |stub| - stub.post('/oauth/token') do - [200, {'Content-Type' => 'application/json'}, JSON.dump('access_token' => 'the-token')] - end - end - - token = client.get_token(parse: 'random') - expect(token).to be_a OAuth2::AccessToken - expect(token.token).to eq('the-token') - end - end - - context 'when parse is correct' do - it 'returns a configured AccessToken' do - client = stubbed_client do |stub| - stub.post('/oauth/token') do - [200, {'Content-Type' => 'application/json'}, JSON.dump('access_token' => 'the-token')] - end - end - - token = client.get_token(parse: :json) - expect(token).to be_a OAuth2::AccessToken - expect(token.token).to eq('the-token') - end - end - - context 'when snaky is falsy, but response is snaky' do - it 'returns a configured AccessToken' do - client = stubbed_client do |stub| - stub.post('/oauth/token') do - [200, {'Content-Type' => 'application/json'}, JSON.dump('access_token' => 'the-token')] - end - end - - token = client.get_token(snaky: false) - expect(token).to be_a OAuth2::AccessToken - expect(token.token).to eq('the-token') - expect(token.response.parsed.to_h).to eq('access_token' => 'the-token') - end - end - - context 'when snaky is falsy, but response is not snaky' do - it 'returns a configured AccessToken' do - client = stubbed_client do |stub| - stub.post('/oauth/token') do - [200, {'Content-Type' => 'application/json'}, JSON.dump('accessToken' => 'the-token')] - end - end - - token = client.get_token({snaky: false}, {param_name: 'accessToken'}) - expect(token).to be_a OAuth2::AccessToken - expect(token.token).to eq('the-token') - expect(token.response.parsed.to_h).to eq('accessToken' => 'the-token') - end - end - - it 'authenticates with request parameters' do - client = stubbed_client(auth_scheme: :request_body) do |stub| - stub.post('/oauth/token', 'client_id' => 'abc', 'client_secret' => 'def') do |_env| - [200, {'Content-Type' => 'application/json'}, JSON.dump('access_token' => 'the-token')] - end - end - client.get_token({}) - end - - it 'authenticates with Basic auth' do - client = stubbed_client(auth_scheme: :basic_auth) do |stub| - stub.post('/oauth/token') do |env| - raise Faraday::Adapter::Test::Stubs::NotFound unless env[:request_headers]['Authorization'] == OAuth2::Authenticator.encode_basic_auth('abc', 'def') - - [200, {'Content-Type' => 'application/json'}, JSON.dump('access_token' => 'the-token')] - end - end - client.get_token({}) - end - - it 'authenticates with JSON' do - client = stubbed_client(auth_scheme: :basic_auth) do |stub| - stub.post('/oauth/token') do |env| - [200, {'Content-Type' => 'application/json'}, JSON.dump('access_token' => 'the-token')] - end - end - client.get_token(headers: {'Content-Type' => 'application/json'}) - end - - it 'sets the response object on the access token' do - client = stubbed_client do |stub| - stub.post('/oauth/token') do - [200, {'Content-Type' => 'application/json'}, JSON.dump('access_token' => 'the-token')] - end - end - - token = client.get_token({}) - expect(token.response).to be_a OAuth2::Response - expect(token.response.parsed).to eq('access_token' => 'the-token') - end - - context 'when the :raise_errors flag is set to false' do - let(:body) { nil } - let(:status_code) { 500 } - let(:content_type) { 'application/json' } - let(:client) do - stubbed_client(raise_errors: false) do |stub| - stub.post('/oauth/token') do - [status_code, {'Content-Type' => content_type}, body] - end - end - end - - context 'when the request body is nil' do - subject(:get_token) { client.get_token({}) } - - it 'raises error JSON::ParserError' do - block_is_expected { get_token }.to raise_error(JSON::ParserError) - end - - context 'when extract_access_token raises an exception' do - let(:status_code) { 200 } - let(:extract_proc) { proc { |client, hash| raise ArgumentError } } - - it 'returns a nil :access_token' do - expect(client.get_token({}, {}, extract_proc)).to eq(nil) - end - end - end - - context 'when status code is 200' do - let(:status_code) { 200 } - - context 'when the request body is not nil' do - let(:body) { JSON.dump('access_token' => 'the-token') } - - it 'returns the parsed :access_token from body' do - token = client.get_token({}) - expect(token.response).to be_a OAuth2::Response - expect(token.response.parsed).to eq('access_token' => 'the-token') - end - end - - context 'when Content-Type is not JSON' do - let(:content_type) { 'text/plain' } - let(:body) { 'hello world' } - - it 'returns the parsed :access_token from body' do - expect(client.get_token({})).to be_nil - end - end - end - end - - describe 'with custom access_token_class option' do - let(:options) { {access_token_class: CustomAccessToken} } - let(:payload) { {'custom_token' => 'the-token'} } - let(:content_type) { 'application/json' } - let(:client) do - stubbed_client(options) do |stub| - stub.post('/oauth/token') do - [200, {'Content-Type' => content_type}, JSON.dump(payload)] - end - end - end - - before do - custom_class = Class.new(OAuth2::AccessToken) do - attr_accessor :response - - def self.from_hash(client, hash) - new(client, hash.delete('custom_token')) - end - - def self.contains_token?(hash) - hash.key?('custom_token') - end - end - - stub_const('CustomAccessToken', custom_class) - end - - it 'returns the parsed :custom_token from body' do - client.get_token({}) - end - - context 'when the :raise_errors flag is set to true' do - let(:options) { {access_token_class: CustomAccessToken, raise_errors: true} } - let(:payload) { {} } - - it 'raises an error' do - expect { client.get_token({}) }.to raise_error(OAuth2::Error) - end - - context 'when the legacy extract_access_token' do - let(:extract_access_token) do - proc do |client, hash| - token = hash['data']['access_token'] - OAuth2::AccessToken.new(client, token, hash) - end - end - let(:options) { {raise_errors: true} } - let(:payload) { {} } - - it 'raises an error' do - expect { client.get_token({}, {}, extract_access_token) }.to raise_error(OAuth2::Error) - end - end - end - - context 'when status code is 200' do - let(:status_code) { 200 } - - context 'when the request body is blank' do - let(:payload) { {} } - - it 'raises an error' do - expect { client.get_token({}) }.to raise_error(OAuth2::Error) - end - end - - context 'when Content-Type is not JSON' do - let(:content_type) { 'text/plain' } - let(:body) { 'hello world' } - - it 'raises an error' do - expect { client.get_token({}) }.to raise_error(OAuth2::Error) - end - end - end - - context 'when access token instance responds to response=' do - let(:options) { {access_token_class: CustomAccessToken, raise_errors: false} } - - it 'sets response' do - expect(client.get_token({}).response).to be_a(OAuth2::Response) - end - end - - context 'when request has a block' do - subject(:request) do - client.get_token({}) do |req| - raise 'Block is executing' - end - end - - let(:options) { {access_token_class: CustomAccessToken, raise_errors: false} } - - it 'sets response' do - block_is_expected.to raise_error('Block is executing') - end - end - end - - describe 'abnormal custom access_token_class option' do - let(:payload) { {'custom_token' => 'the-token'} } - let(:content_type) { 'application/json' } - let(:client) do - stubbed_client(options) do |stub| - stub.post('/oauth/token') do - [200, {'Content-Type' => content_type}, JSON.dump(payload)] - end - end - end - - before do - custom_class = Class.new do - def initialize(client, hash) - end - - def self.from_hash(client, hash) - new(client, hash.delete('custom_token')) - end - - def self.contains_token?(hash) - hash.key?('custom_token') - end - end - - stub_const('StrangeAccessToken', custom_class) - end - - context 'when the :raise_errors flag is set to true' do - let(:options) { {access_token_class: StrangeAccessToken, raise_errors: true} } - let(:payload) { {} } - - it 'raises an error' do - expect { client.get_token({}) }.to raise_error(OAuth2::Error) - end - end - - context 'when access token instance does not responds to response=' do - let(:options) { {access_token_class: StrangeAccessToken} } - let(:payload) { {'custom_token' => 'the-token'} } - - it 'sets response' do - token_access = client.get_token({}) - expect(token_access).to be_a(StrangeAccessToken) - expect(token_access).not_to respond_to(:response=) - expect(token_access).not_to respond_to(:response) - end - end - - context 'when request has a block' do - subject(:request) do - client.get_token({}) do |req| - raise 'Block is executing' - end - end - - let(:options) { {access_token_class: StrangeAccessToken} } - - it 'sets response' do - block_is_expected.to raise_error('Block is executing') - end - end - end - - describe 'with extract_access_token option' do - let(:client) do - stubbed_client(extract_access_token: extract_access_token) do |stub| - stub.post('/oauth/token') do - [200, {'Content-Type' => 'application/json'}, JSON.dump('data' => {'access_token' => 'the-token'})] - end - end - end - - let(:extract_access_token) do - proc do |client, hash| - token = hash['data']['access_token'] - OAuth2::AccessToken.new(client, token, hash) - end - end - - it 'returns a configured AccessToken' do - token = client.get_token({}) - expect(token).to be_a OAuth2::AccessToken - expect(token.token).to eq('the-token') - end - - context 'with deprecation' do - subject(:printed) do - capture(:stderr) do - client.get_token({}) - end - end - - it 'warns on STDERR' do - msg = <<-msg.lstrip - OAuth2::Client#initialize argument `extract_access_token` will be removed in oauth2 v3. Refactor to use `access_token_class`. - MSG - expect(printed).to eq(msg) - end - - context 'on request' do - subject(:printed) do - capture(:stderr) do - client.get_token({}, {}, extract_access_token) - end - end - - let(:client) do - stubbed_client do |stub| - stub.post('/oauth/token') do - [200, {'Content-Type' => 'application/json'}, JSON.dump('data' => {'access_token' => 'the-token'})] - end - end - end - - it 'warns on STDERR' do - msg = <<-msg.lstrip - OAuth2::Client#get_token argument `extract_access_token` will be removed in oauth2 v3. Refactor to use `access_token_class` on #initialize. - MSG - expect(printed).to eq(msg) - end - end - end - end - - it 'forwards given token parameters' do - client = stubbed_client(auth_scheme: :request_body) do |stub| - stub.post('/oauth/token', 'arbitrary' => 'parameter', 'client_id' => 'abc', 'client_secret' => 'def') do |_env| - [200, {'Content-Type' => 'application/json'}, JSON.dump('access_token' => 'the-token')] - end - end - client.get_token({'arbitrary' => 'parameter'}) # rubocop:disable Style/BracesAroundHashParameters - end - - context 'when token_method is set to post_with_query_string' do - it 'uses the http post method and passes parameters in the query string' do - client = stubbed_client(token_method: :post_with_query_string) do |stub| - stub.post('/oauth/token?state=abc123') do |_env| - [200, {'Content-Type' => 'application/json'}, JSON.dump('access_token' => 'the-token')] - end - end - client.get_token({'state' => 'abc123'}) # rubocop:disable Style/BracesAroundHashParameters - end - end - - def stubbed_client(params = {}, &stubs) - params = {site: 'https://api.example.com'}.merge(params) - OAuth2::Client.new('abc', 'def', params) do |builder| - builder.adapter :test, &stubs - end - end - end - - it 'instantiates an HTTP Method with this client' do - expect(subject.http_method).to be_kind_of(Symbol) - end - - it 'instantiates an AuthCode strategy with this client' do - expect(subject.auth_code).to be_kind_of(OAuth2::Strategy::AuthCode) - end - - it 'instantiates an Implicit strategy with this client' do - expect(subject.implicit).to be_kind_of(OAuth2::Strategy::Implicit) - end - - context 'with SSL options' do - subject do - cli = described_class.new('abc', 'def', site: 'https://api.example.com', ssl: {ca_file: 'foo.pem'}) - cli.connection = Faraday.new(cli.site, cli.options[:connection_opts]) do |b| - b.adapter :test - end - cli - end - - it 'passes the SSL options along to Faraday::Connection#ssl' do - expect(subject.connection.ssl.fetch(:ca_file)).to eq('foo.pem') - end - end - - context 'without a connection-configuration block' do - subject do - described_class.new('abc', 'def', site: 'https://api.example.com') - end - - it 'applies default faraday middleware to the connection' do - expect(subject.connection.builder.handlers).to include(Faraday::Request::UrlEncoded) - end - end -end diff --git a/spec/oauth2/error_spec.rb b/spec/oauth2/error_spec.rb deleted file mode 100644 index fb1ed492..00000000 --- a/spec/oauth2/error_spec.rb +++ /dev/null @@ -1,645 +0,0 @@ -# encoding: UTF-8 -# frozen_string_literal: true - -class StirredHash < Hash - def to_str - '{"hello":"� Cool � StirredHash"}' - end -end - -class XmledString < String - XML = ' - - -� Cool � XmledString - - -'.freeze - def to_str - XML - end -end - -RSpec.describe OAuth2::Error do - subject { described_class.new(response) } - - let(:response) do - raw_response = Faraday::Response.new( - status: 418, - response_headers: response_headers, - body: response_body - ) - - OAuth2::Response.new(raw_response) - end - - let(:response_headers) { {'Content-Type' => 'application/json'} } - let(:response_body) { {text: 'Coffee brewing failed'}.to_json } - - it 'sets the response object to #response on self' do - error = described_class.new(response) - expect(error.response).to equal(response) - end - - describe 'attr_readers' do - it 'has code' do - expect(subject).to respond_to(:code) - end - - it 'has description' do - expect(subject).to respond_to(:description) - end - - it 'has response' do - expect(subject).to respond_to(:response) - end - end - - context 'when the response is parsed' do - let(:response_body) { response_hash.to_json } - let(:response_hash) { {text: 'Coffee brewing failed'} } - - context 'when the response has an error and error_description' do - before do - response_hash['error_description'] = 'Short and stout' - response_hash['error'] = 'i_am_a_teapot' - end - - it 'sets the code attribute' do - expect(subject.code).to eq('i_am_a_teapot') - end - - it 'sets the description attribute' do - expect(subject.description).to eq('Short and stout') - end - - it 'prepends to the error message with a return character' do - expect(subject.message.each_line.to_a).to eq( - [ - "i_am_a_teapot: Short and stout\n", - '{"text":"Coffee brewing failed","error_description":"Short and stout","error":"i_am_a_teapot"}', - ] - ) - end - - context 'when the response needs to be encoded' do - let(:response_body) { JSON.dump(response_hash).force_encoding('ASCII-8BIT') } - - context 'with invalid characters present' do - before do - response_body.gsub!('stout', "255円 invalid 255円") - end - - it 'replaces them' do - # The skip can be removed once support for < 2.1 is dropped. - encoding = {reason: 'encode/scrub only works as of Ruby 2.1'} - skip_for(encoding.merge(engine: 'jruby')) - # See https://bibwild.wordpress.com/2013/03/12/removing-illegal-bytes-for-encoding-in-ruby-1-9-strings/ - - raise 'Invalid characters not replaced' unless subject.message.include?('� invalid �') - # This will fail if {:invalid => replace} is not passed into `encode` - end - end - - context 'with undefined characters present' do - before do - response_hash['error_description'] += ": 'A magical voyage of tea 🍵'" - end - - it 'replaces them' do - raise 'Undefined characters not replaced' unless subject.message.include?('tea �') - # This will fail if {:undef => replace} is not passed into `encode` - end - end - end - - context 'when the response is not an encodable thing' do - let(:response_headers) { {'Content-Type' => 'who knows'} } - let(:response_body) { {text: 'Coffee brewing failed'} } - - before do - expect(response_body).not_to respond_to(:encode) - # i.e. a Ruby hash - end - - it 'does not try to encode the message string' do - expect(subject.message).to eq(response_body.to_s) - end - end - - context 'when using :json parser with non-encodable data' do - let(:response_headers) { {'Content-Type' => 'application/hal+json'} } - let(:response_body) do - StirredHash.new( - "_links": { - "self": {"href": '/orders/523'}, - "warehouse": {"href": '/warehouse/56'}, - "invoice": {"href": '/invoices/873'}, - }, - "currency": 'USD', - "status": 'shipped', - "total": 10.20 - ) - end - - before do - expect(response_body).not_to respond_to(:force_encoding) - expect(response_body).to respond_to(:to_str) - end - - it 'does not force encode the message' do - expect(subject.message).to eq('{"hello":"� Cool � StirredHash"}') - end - end - - context 'when using :xml parser' do - let(:response_headers) { {'Content-Type' => 'text/xml'} } - let(:response_body) do - XmledString.new(XmledString::XML) - end - - before do - expect(response_body).to respond_to(:to_str) - end - - it 'parses the XML' do - expect(subject.message).to eq(XmledString::XML) - end - end - - context 'when using :xml parser with non-String-like thing' do - let(:response_headers) { {'Content-Type' => 'text/xml'} } - let(:response_body) { {hello: :world} } - - before do - expect(response_body).not_to respond_to(:to_str) - end - - it 'just returns the thing if it can' do - expect(subject.message).to eq('{:hello=>:world}') - end - end - end - - it 'sets the code attribute to nil' do - expect(subject.code).to be_nil - end - - it 'sets the description attribute' do - expect(subject.description).to be_nil - end - - context 'when there is no error description' do - before do - expect(response_hash).not_to have_key('error') - expect(response_hash).not_to have_key('error_description') - end - - it 'does not prepend anything to the message' do - expect(subject.message.lines.count).to eq(1) - expect(subject.message).to eq '{"text":"Coffee brewing failed"}' - end - - it 'does not set code' do - expect(subject.code).to be_nil - end - - it 'does not set description' do - expect(subject.description).to be_nil - end - end - - context 'when there is code (error)' do - before do - response_hash['error_description'] = 'Short and stout' - response_hash['error'] = 'i_am_a_teapot' - response_hash['status'] = '418' - end - - it 'prepends to the error message with a return character' do - expect(subject.message.each_line.to_a).to eq( - [ - "i_am_a_teapot: Short and stout\n", - { - "text": 'Coffee brewing failed', - "error_description": 'Short and stout', - "error": 'i_am_a_teapot', - "status": '418', - }.to_json, - ] - ) - end - - context 'when the response needs to be encoded' do - let(:response_body) { JSON.dump(response_hash).force_encoding('ASCII-8BIT') } - - context 'with invalid characters present' do - before do - response_body.gsub!('stout', "255円 invalid 255円") - end - - it 'replaces them' do - # The skip can be removed once support for < 2.1 is dropped. - encoding = {reason: 'encode/scrub only works as of Ruby 2.1'} - skip_for(encoding.merge(engine: 'jruby')) - # See https://bibwild.wordpress.com/2013/03/12/removing-illegal-bytes-for-encoding-in-ruby-1-9-strings/ - - raise 'Invalid characters not replaced' unless subject.message.include?('� invalid �') - # This will fail if {:invalid => replace} is not passed into `encode` - end - end - - context 'with undefined characters present' do - before do - response_hash['error_description'] += ": 'A magical voyage of tea 🍵'" - end - - it 'replaces them' do - raise 'Undefined characters not replaced' unless subject.message.include?('tea �') - # This will fail if {:undef => replace} is not passed into `encode` - end - end - end - - context 'when the response is not an encodable thing' do - let(:response_headers) { {'Content-Type' => 'who knows'} } - let(:response_body) { {text: 'Coffee brewing failed'} } - - before do - expect(response_body).not_to respond_to(:encode) - # i.e. a Ruby hash - end - - it 'does not try to encode the message string' do - expect(subject.message).to eq(response_body.to_s) - end - end - - it 'sets the code attribute' do - expect(subject.code).to eq('i_am_a_teapot') - end - - it 'sets the description attribute' do - expect(subject.description).to eq('Short and stout') - end - end - - context 'when there is code (error) but no error_description' do - before do - response_hash.delete('error_description') - response_hash['error'] = 'i_am_a_teapot' - response_hash['status'] = '418' - end - - it 'prepends to the error message with a return character' do - expect(subject.message.each_line.to_a).to eq( - [ - "i_am_a_teapot: \n", - { - "text": 'Coffee brewing failed', - "error": 'i_am_a_teapot', - "status": '418', - }.to_json, - ] - ) - end - - context 'when the response needs to be encoded' do - let(:response_body) { JSON.dump(response_hash).force_encoding('ASCII-8BIT') } - - context 'with invalid characters present' do - before do - response_body.gsub!('brewing', "255円 invalid 255円") - end - - it 'replaces them' do - # The skip can be removed once support for < 2.1 is dropped. - encoding = {reason: 'encode/scrub only works as of Ruby 2.1'} - skip_for(encoding.merge(engine: 'jruby')) - # See https://bibwild.wordpress.com/2013/03/12/removing-illegal-bytes-for-encoding-in-ruby-1-9-strings/ - - raise 'Invalid characters not replaced' unless subject.message.include?('� invalid �') - # This will fail if {:invalid => replace} is not passed into `encode` - end - end - end - - context 'when the response is not an encodable thing' do - let(:response_headers) { {'Content-Type' => 'who knows'} } - let(:response_body) { {text: 'Coffee brewing failed'} } - - before do - expect(response_body).not_to respond_to(:encode) - # i.e. a Ruby hash - end - - it 'does not try to encode the message string' do - expect(subject.message).to eq(response_body.to_s) - end - end - - it 'sets the code attribute from error' do - expect(subject.code).to eq('i_am_a_teapot') - end - - it 'does not set the description attribute' do - expect(subject.description).to be_nil - end - end - - context 'when there is error_description but no code (error)' do - before do - response_hash['error_description'] = 'Short and stout' - response_hash.delete('error') - end - - it 'prepends to the error message with a return character' do - expect(subject.message.each_line.to_a).to eq( - [ - "Short and stout\n", - { - "text": 'Coffee brewing failed', - "error_description": 'Short and stout', - }.to_json, - ] - ) - end - - context 'when the response needs to be encoded' do - let(:response_body) { JSON.dump(response_hash).force_encoding('ASCII-8BIT') } - - context 'with invalid characters present' do - before do - response_body.gsub!('stout', "255円 invalid 255円") - end - - it 'replaces them' do - # The skip can be removed once support for < 2.1 is dropped. - encoding = {reason: 'encode/scrub only works as of Ruby 2.1'} - skip_for(encoding.merge(engine: 'jruby')) - # See https://bibwild.wordpress.com/2013/03/12/removing-illegal-bytes-for-encoding-in-ruby-1-9-strings/ - - raise 'Invalid characters not replaced' unless subject.message.include?('� invalid �') - # This will fail if {:invalid => replace} is not passed into `encode` - end - end - - context 'with undefined characters present' do - before do - response_hash['error_description'] += ": 'A magical voyage of tea 🍵'" - end - - it 'replaces them' do - raise 'Undefined characters not replaced' unless subject.message.include?('tea �') - # This will fail if {:undef => replace} is not passed into `encode` - end - end - end - - context 'when the response is not an encodable thing' do - let(:response_headers) { {'Content-Type' => 'who knows'} } - let(:response_body) { {text: 'Coffee brewing failed'} } - - before do - expect(response_body).not_to respond_to(:encode) - # i.e. a Ruby hash - end - - it 'does not try to encode the message string' do - expect(subject.message).to eq(response_body.to_s) - end - end - - it 'sets the code attribute' do - expect(subject.code).to be_nil - end - - it 'sets the description attribute' do - expect(subject.description).to eq('Short and stout') - end - end - end - - context 'when the response is simple hash, not parsed' do - subject { described_class.new(response_hash) } - - let(:response_hash) { {text: 'Coffee brewing failed'} } - - it 'sets the code attribute to nil' do - expect(subject.code).to be_nil - end - - it 'sets the description attribute' do - expect(subject.description).to be_nil - end - - context 'when the response has an error and error_description' do - before do - response_hash['error_description'] = 'Short and stout' - response_hash['error'] = 'i_am_a_teapot' - end - - it 'sets the code attribute' do - expect(subject.code).to eq('i_am_a_teapot') - end - - it 'sets the description attribute' do - expect(subject.description).to eq('Short and stout') - end - - it 'prepends to the error message with a return character' do - expect(subject.message.each_line.to_a).to eq( - [ - "i_am_a_teapot: Short and stout\n", - '{:text=>"Coffee brewing failed", "error_description"=>"Short and stout", "error"=>"i_am_a_teapot"}', - ] - ) - end - - context 'when using :xml parser with non-String-like thing' do - let(:response_headers) { {'Content-Type' => 'text/xml'} } - let(:response_hash) { {hello: :world} } - - before do - expect(response_hash).not_to respond_to(:to_str) - end - - it 'just returns whatever it can' do - expect(subject.message).to eq("i_am_a_teapot: Short and stout\n{:hello=>:world, \"error_description\"=>\"Short and stout\", \"error\"=>\"i_am_a_teapot\"}") - end - end - end - - context 'when using :xml parser with non-String-like thing' do - let(:response_headers) { {'Content-Type' => 'text/xml'} } - let(:response_hash) { {hello: :world} } - - before do - expect(response_hash).not_to respond_to(:to_str) - end - - it 'just returns the thing if it can' do - expect(subject.message).to eq('{:hello=>:world}') - end - end - - context 'when there is no error description' do - before do - expect(response_hash).not_to have_key('error') - expect(response_hash).not_to have_key('error_description') - end - - it 'does not prepend anything to the message' do - expect(subject.message.lines.count).to eq(1) - expect(subject.message).to eq '{:text=>"Coffee brewing failed"}' - end - - it 'does not set code' do - expect(subject.code).to be_nil - end - - it 'does not set description' do - expect(subject.description).to be_nil - end - end - - context 'when there is code (error)' do - before do - response_hash['error_description'] = 'Short and stout' - response_hash['error'] = 'i_am_a_teapot' - response_hash['status'] = '418' - end - - it 'prepends to the error message with a return character' do - expect(subject.message.each_line.to_a).to eq( - [ - "i_am_a_teapot: Short and stout\n", - '{:text=>"Coffee brewing failed", "error_description"=>"Short and stout", "error"=>"i_am_a_teapot", "status"=>"418"}', - ] - ) - end - - it 'sets the code attribute' do - expect(subject.code).to eq('i_am_a_teapot') - end - - it 'sets the description attribute' do - expect(subject.description).to eq('Short and stout') - end - end - - context 'when there is code (error) but no error_description' do - before do - response_hash.delete('error_description') - response_hash['error'] = 'i_am_a_teapot' - response_hash['status'] = '418' - end - - it 'sets the code attribute from error' do - expect(subject.code).to eq('i_am_a_teapot') - end - - it 'does not set the description attribute' do - expect(subject.description).to be_nil - end - - it 'prepends to the error message with a return character' do - expect(subject.message.each_line.to_a).to eq( - [ - "i_am_a_teapot: \n", - '{:text=>"Coffee brewing failed", "error"=>"i_am_a_teapot", "status"=>"418"}', - ] - ) - end - end - - context 'when there is error_description but no code (error)' do - before do - response_hash['error_description'] = 'Short and stout' - response_hash.delete('error') - end - - it 'prepends to the error message with a return character' do - expect(subject.message.each_line.to_a).to eq( - [ - "Short and stout\n", - '{:text=>"Coffee brewing failed", "error_description"=>"Short and stout"}', - ] - ) - end - - context 'when the response is not an encodable thing' do - let(:response_headers) { {'Content-Type' => 'who knows'} } - let(:response_hash) { {text: 'Coffee brewing failed'} } - - before do - expect(response_hash).not_to respond_to(:encode) - # i.e. a Ruby hash - end - - it 'does not try to encode the message string' do - expect(subject.message).to eq("Short and stout\n{:text=>\"Coffee brewing failed\", \"error_description\"=>\"Short and stout\"}") - end - end - - it 'sets the code attribute' do - expect(subject.code).to be_nil - end - - it 'sets the description attribute' do - expect(subject.description).to eq('Short and stout') - end - end - end - - context 'when the response is not a hash, not parsed' do - subject { described_class.new(response_thing) } - - let(:response_thing) { [200, 'Success'] } - - it 'sets the code attribute to nil' do - expect(subject.code).to be_nil - end - - it 'sets the description attribute' do - expect(subject.description).to be_nil - end - - it 'sets the body attribute' do - expect(subject.body).to eq(response_thing) - end - - it 'sets the response attribute' do - expect(subject.response).to eq(response_thing) - end - end - - context 'when the response does not parse to a hash' do - let(:response_headers) { {'Content-Type' => 'text/html'} } - let(:response_body) { ' Hello, I am a teapot' } - - before do - expect(response.parsed).not_to be_a(Hash) - end - - it 'does not do anything to the message' do - expect(subject.message.lines.count).to eq(1) - expect(subject.message).to eq(response_body) - end - - it 'does not set code' do - expect(subject.code).to be_nil - end - - it 'does not set description' do - expect(subject.description).to be_nil - end - end - - describe 'parsing json' do - it 'does not blow up' do - expect { subject.to_json }.not_to raise_error - expect { subject.response.to_json }.not_to raise_error - end - end -end diff --git a/spec/oauth2/response_spec.rb b/spec/oauth2/response_spec.rb deleted file mode 100644 index 4ceb228f..00000000 --- a/spec/oauth2/response_spec.rb +++ /dev/null @@ -1,345 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe OAuth2::Response do - subject { described_class.new(raw_response) } - - let(:raw_response) { Faraday::Response.new(status: status, response_headers: headers, body: body) } - let(:status) { 200 } - let(:headers) { {'foo' => 'bar'} } - let(:body) { 'foo' } - - describe '#initialize' do - it 'returns the status, headers and body' do - expect(subject.headers).to eq(headers) - expect(subject.status).to eq(status) - expect(subject.body).to eq(body) - end - end - - describe '.register_parser' do - let(:response) do - double('response', headers: {'Content-Type' => 'application/foo-bar'}, - status: 200, - body: 'baz') - end - - before do - described_class.register_parser(:foobar, ['application/foo-bar']) do |body| - "foobar #{body}" - end - end - - it 'adds to the content types and parsers' do - expect(described_class.send(:class_variable_get, :@@parsers).keys).to include(:foobar) - expect(described_class.send(:class_variable_get, :@@content_types).keys).to include('application/foo-bar') - end - - it 'is able to parse that content type automatically' do - expect(described_class.new(response).parsed).to eq('foobar baz') - end - end - - describe '#content_type' do - context 'when headers are blank' do - let(:headers) { nil } - - it 'returns nil' do - expect(subject.content_type).to be_nil - end - end - - context 'when content-type is not present' do - let(:headers) { {'a fuzzy' => 'fuzzer'} } - - it 'returns empty string' do - expect(subject.content_type).to eq('') - end - end - - context 'when content-type is present' do - let(:headers) { {'Content-Type' => 'application/x-www-form-urlencoded'} } - - it 'returns the content type header contents' do - expect(subject.content_type).to eq('application/x-www-form-urlencoded') - end - end - end - - describe '#parsed' do - subject(:parsed) do - headers = {'Content-Type' => content_type} - response = double('response', headers: headers, body: body) - instance = described_class.new(response) - instance.parsed - end - - shared_examples_for 'parsing JSON-like' do - it 'has num keys' do - expect(parsed.keys.size).to eq(6) - end - - it 'parses string' do - expect(parsed['foo']).to eq('bar') - expect(parsed.key('bar')).to eq('foo') - end - - it 'parses non-zero number' do - expect(parsed['answer']).to eq(42) - expect(parsed.key(42)).to eq('answer') - end - - it 'parses nil as NilClass' do - expect(parsed['krill']).to be_nil - expect(parsed.key(nil)).to eq('krill') - end - - it 'parses zero as number' do - expect(parsed['zero']).to eq(0) - expect(parsed.key(0)).to eq('zero') - end - - it 'parses false as FalseClass' do - expect(parsed['malign']).to be(false) - expect(parsed.key(false)).to eq('malign') - end - - it 'parses false as TrueClass' do - expect(parsed['shine']).to be(true) - expect(parsed.key(true)).to eq('shine') - end - end - - context 'when application/json' do - let(:content_type) { 'application/json' } - let(:body) { JSON.dump(foo: 'bar', answer: 42, krill: nil, zero: 0, malign: false, shine: true) } - - it_behaves_like 'parsing JSON-like' - end - - context 'when application/Json' do - let(:content_type) { 'application/Json' } - let(:body) { JSON.dump(foo: 'bar', answer: 42, krill: nil, zero: 0, malign: false, shine: true) } - - it_behaves_like 'parsing JSON-like' - end - - context 'when application/hal+json' do - let(:content_type) { 'application/hal+json' } - let(:body) { JSON.dump(foo: 'bar', answer: 42, krill: nil, zero: 0, malign: false, shine: true) } - - it_behaves_like 'parsing JSON-like' - end - - context 'when application/x-www-form-urlencoded' do - let(:content_type) { 'application/x-www-form-urlencoded' } - let(:body) { 'foo=bar&answer=42&krill=&zero=0&malign=false&shine=true' } - - it 'has num keys' do - expect(parsed.keys.size).to eq(6) - end - - it 'parses string' do - expect(parsed['foo']).to eq('bar') - expect(parsed.key('bar')).to eq('foo') - end - - it 'parses non-zero number as string' do - expect(parsed['answer']).to eq('42') - expect(parsed.key('42')).to eq('answer') - end - - it 'parses nil as empty string' do - expect(parsed['krill']).to eq('') - expect(parsed.key('')).to eq('krill') - end - - it 'parses zero as string' do - expect(parsed['zero']).to eq('0') - expect(parsed.key('0')).to eq('zero') - end - - it 'parses false as string' do - expect(parsed['malign']).to eq('false') - expect(parsed.key('false')).to eq('malign') - end - - it 'parses true as string' do - expect(parsed['shine']).to eq('true') - expect(parsed.key('true')).to eq('shine') - end - end - - it 'parses application/vnd.collection+json body' do - headers = {'Content-Type' => 'application/vnd.collection+json'} - body = JSON.dump(collection: {}) - response = double('response', headers: headers, body: body) - subject = described_class.new(response) - expect(subject.parsed.keys.size).to eq(1) - end - - it 'parses application/vnd.api+json body' do - headers = {'Content-Type' => 'application/vnd.api+json'} - body = JSON.dump(collection: {}) - response = double('response', headers: headers, body: body) - subject = described_class.new(response) - expect(subject.parsed.keys.size).to eq(1) - end - - it 'parses application/problem+json body' do - headers = {'Content-Type' => 'application/problem+json'} - body = JSON.dump(type: 'https://tools.ietf.org/html/rfc7231#section-6.5.4', title: 'Not Found') - response = double('response', headers: headers, body: body) - subject = described_class.new(response) - expect(subject.parsed.keys.size).to eq(2) - expect(subject.parsed['type']).to eq('https://tools.ietf.org/html/rfc7231#section-6.5.4') - expect(subject.parsed['title']).to eq('Not Found') - end - - it "doesn't try to parse other content-types" do - headers = {'Content-Type' => 'text/html'} - body = '' - - response = double('response', headers: headers, body: body) - - expect(JSON).not_to receive(:parse) - expect(Rack::Utils).not_to receive(:parse_query) - - subject = described_class.new(response) - expect(subject.parsed).to be_nil - end - - it "doesn't parse bodies which have previously been parsed" do - headers = {'Content-Type' => 'application/json'} - body = {foo: 'bar', answer: 42, krill: nil, zero: 0, malign: false, shine: true} - - response = double('response', headers: headers, body: body) - - expect(JSON).not_to receive(:parse) - expect(Rack::Utils).not_to receive(:parse_query) - - subject = described_class.new(response) - expect(subject.parsed.keys.size).to eq(6) - end - - it 'snakecases json keys when parsing' do - headers = {'Content-Type' => 'application/json'} - body = JSON.dump('accessToken' => 'bar', 'MiGever' => 'Ani') - response = double('response', headers: headers, body: body) - subject = described_class.new(response) - expect(subject.parsed.keys.size).to eq(2) - expect(subject.parsed['access_token']).to eq('bar') - expect(subject.parsed['mi_gever']).to eq('Ani') - end - - context 'when not snaky' do - it 'does not snakecase json keys when parsing' do - headers = {'Content-Type' => 'application/json'} - body = JSON.dump('accessToken' => 'bar', 'MiGever' => 'Ani') - response = double('response', headers: headers, body: body) - subject = described_class.new(response, snaky: false) - expect(subject.parsed.keys.size).to eq(2) - expect(subject.parsed['accessToken']).to eq('bar') - expect(subject.parsed['MiGever']).to eq('Ani') - expect(subject.parsed['access_token']).to be_nil - expect(subject.parsed['mi_gever']).to be_nil - end - end - - it 'supports registered parsers with arity == 0; passing nothing' do - described_class.register_parser(:arity_0, []) do - 'a-ok' - end - - headers = {'Content-Type' => 'text/html'} - body = '' - response = double('response', headers: headers, body: body) - - subject = described_class.new(response, parse: :arity_0) - - expect(subject.parsed).to eq('a-ok') - end - - it 'supports registered parsers with arity == 2; passing body and response' do - headers = {'Content-Type' => 'text/html'} - body = '' - response = double('response', headers: headers, body: body) - - described_class.register_parser(:arity_2, []) do |passed_body, passed_response| - expect(passed_body).to eq(body) - expect(passed_response).to eq(response) - - 'a-ok' - end - - subject = described_class.new(response, parse: :arity_2) - - expect(subject.parsed).to eq('a-ok') - end - - it 'supports registered parsers with arity> 2; passing body and response' do - headers = {'Content-Type' => 'text/html'} - body = '' - response = double('response', headers: headers, body: body) - - described_class.register_parser(:arity_3, []) do |passed_body, passed_response, *args| - expect(passed_body).to eq(body) - expect(passed_response).to eq(response) - expect(args).to eq([]) - - 'a-ok' - end - - subject = described_class.new(response, parse: :arity_3) - - expect(subject.parsed).to eq('a-ok') - end - - it 'supports directly passed parsers' do - headers = {'Content-Type' => 'text/html'} - body = '' - response = double('response', headers: headers, body: body) - - subject = described_class.new(response, parse: -> { 'a-ok' }) - - expect(subject.parsed).to eq('a-ok') - end - - it 'supports no parsing' do - headers = {'Content-Type' => 'text/html'} - body = '' - response = double('response', headers: headers, body: body) - - subject = described_class.new(response, parse: false) - - expect(subject.parsed).to eq(nil) - end - end - - context 'with xml parser registration' do - it 'tries to load multi_xml.rb and use it' do - expect(described_class.send(:class_variable_get, :@@parsers)[:xml]).not_to be_nil - end - - it 'is able to parse xml' do - headers = {'Content-Type' => 'text/xml'} - body = 'baz' - - response = double('response', headers: headers, body: body) - expect(described_class.new(response).parsed).to eq('foo' => {'bar' => 'baz'}) - end - - it 'is able to parse application/xml' do - headers = {'Content-Type' => 'application/xml'} - body = 'baz' - - response = double('response', headers: headers, body: body) - expect(described_class.new(response).parsed).to eq('foo' => {'bar' => 'baz'}) - end - end - - describe 'converting to json' do - it 'does not blow up' do - expect { subject.to_json }.not_to raise_error - end - end -end diff --git a/spec/oauth2/snaky_hash_spec.rb b/spec/oauth2/snaky_hash_spec.rb deleted file mode 100644 index 32bf0c33..00000000 --- a/spec/oauth2/snaky_hash_spec.rb +++ /dev/null @@ -1,163 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe OAuth2::SnakyHash do - subject(:instance) { described_class.new } - - describe '.build' do - context 'when build from hash' do - subject { described_class.new('AccessToken' => '1') } - - it 'create correct snake hash' do - expect(subject).to be_a(described_class) - expect(subject['AccessToken']).to eq('1') - expect(subject['access_token']).to eq('1') - end - end - - context 'when build from snake_hash' do - subject do - h = described_class.new - h['AccessToken'] = '1' - - described_class.new(h) - end - - it 'create correct snake hash' do - expect(subject).to be_a(described_class) - expect(subject['AccessToken']).to eq('1') - expect(subject['access_token']).to eq('1') - end - end - end - - describe 'assign and query' do - it 'returns assigned value with camel key' do - subject['AccessToken'] = '1' - - expect(subject['AccessToken']).to eq('1') - expect(subject['access_token']).to eq('1') - end - - it 'returns assigned value with snake key' do - subject['access_token'] = '1' - - expect(subject['AccessToken']).to eq('1') - expect(subject['access_token']).to eq('1') - end - - it 'overwrite by alternate key' do - subject['AccessToken'] = '1' - - expect(subject['AccessToken']).to eq('1') - expect(subject['access_token']).to eq('1') - - subject['access_token'] = '2' - - expect(subject['AccessToken']).to eq('2') - expect(subject['access_token']).to eq('2') - end - end - - describe '#to_h' do - context 'when nil' do - it 'can be converted to empty hash' do - expect(instance.to_h).to eq({}) - end - end - - context 'when empty' do - subject(:instance) { described_class.new(original) } - - let(:original) { {} } - - it 'can be converted to empty hash' do - expect(instance.to_h).to eq({}) - end - end - - context 'when not empty' do - subject(:instance) { described_class.new(original) } - - let(:original) { {'a' => 'b', 'bTo' => 'aDo', 'v_rt' => 1, yy_yy: 'yy_yy', :LuLu => :CRays} } - - it 'converts to snake hash' do - expect(instance.to_h).to eq('a' => 'b', 'b_to' => 'aDo', 'lu_lu' => :CRays, 'v_rt' => 1, 'yy_yy' => 'yy_yy') - end - end - end - - describe '#fetch' do - context 'when Camel case key' do - subject { described_class.new('AccessToken' => '1') } - - it 'return correct token' do - expect(subject.fetch('access_token')).to eq('1') - end - end - - context 'when Camel case key with down-cased first letter' do - subject { described_class.new('accessToken' => '1') } - - it 'return correct token' do - expect(subject.fetch('access_token')).to eq('1') - end - end - - context 'when snake case key' do - subject { described_class.new('access_token' => '1') } - - it 'return correct token' do - expect(subject.fetch('access_token')).to eq('1') - end - end - - context 'when missing any key' do - subject { described_class.new } - - it 'raise KeyError with key' do - pending_for(engine: 'jruby', versions: '3.1.0', reason: 'https://github.com/jruby/jruby/issues/7112') - expect do - subject.fetch('access_token') - end.to raise_error(KeyError, /access_token/) - end - - it 'return default value' do - expect(subject.fetch('access_token', 'default')).to eq('default') - end - end - end - - describe '#key?' do - context 'when Camel case key' do - subject { described_class.new('AccessToken' => '1') } - - it 'return true' do - expect(subject.key?('access_token')).to be(true) - end - end - - context 'when Camel case key with down-cased first letter' do - subject { described_class.new('accessToken' => '1') } - - it 'return true' do - expect(subject.key?('access_token')).to be(true) - end - end - - context 'when snake case key' do - subject { described_class.new('access_token' => '1') } - - it 'return true' do - expect(subject.key?('access_token')).to be(true) - end - end - - context 'when missing any key' do - subject { described_class.new } - - it 'return false' do - expect(subject.key?('access_token')).to be(false) - end - end - end -end diff --git a/spec/oauth2/strategy/assertion_spec.rb b/spec/oauth2/strategy/assertion_spec.rb deleted file mode 100644 index 43e498eb..00000000 --- a/spec/oauth2/strategy/assertion_spec.rb +++ /dev/null @@ -1,265 +0,0 @@ -# frozen_string_literal: true - -require 'openssl' -require 'jwt' - -RSpec.describe OAuth2::Strategy::Assertion do - let(:client_assertion) { client.assertion } - - let(:client) do - cli = OAuth2::Client.new('abc', 'def', site: 'http://api.example.com', auth_scheme: auth_scheme) - cli.connection = Faraday.new(cli.site, cli.options[:connection_opts]) do |b| - b.request :url_encoded - b.adapter :test do |stub| - stub.post('/oauth/token') do |token_request| - @request_body = Rack::Utils.parse_nested_query(token_request.body).transform_keys(&:to_sym) - - case @response_format - when 'formencoded' - [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, 'expires_in=600&access_token=salmon&refresh_token=trout'] - when 'json' - [200, {'Content-Type' => 'application/json'}, '{"expires_in":600,"access_token":"salmon","refresh_token":"trout"}'] - else - raise 'Please define @response_format to choose a response content type!' - end - end - end - end - cli - end - - let(:auth_scheme) { :request_body } - - describe '#authorize_url' do - it 'raises NotImplementedError' do - expect { client_assertion.authorize_url }.to raise_error(NotImplementedError) - end - end - - describe '#get_token' do - let(:algorithm) { 'HS256' } - let(:key) { 'arowana' } - let(:timestamp) { Time.now.to_i } - let(:claims) do - { - iss: 'carp@example.com', - scope: 'https://oauth.example.com/auth/flounder', - aud: 'https://sturgeon.example.com/oauth2/token', - exp: timestamp + 3600, - iat: timestamp, - sub: '12345', - custom_claim: 'ling cod', - } - end - - before do - @response_format = 'json' - end - - describe 'assembling a JWT assertion' do - let(:jwt) do - payload, header = JWT.decode(@request_body[:assertion], key, true, algorithm: algorithm) - {payload: payload, header: header} - end - - let(:payload) { jwt[:payload] } - let(:header) { jwt[:header] } - - shared_examples_for 'encodes the JWT' do - it 'indicates algorithm in the header' do - expect(header).not_to be_nil - expect(header['alg']).to eq(algorithm) - end - - it 'has claims' do - expect(payload).not_to be_nil - expect(payload.keys).to match_array(%w[iss scope aud exp iat sub custom_claim]) - payload.each do |key, claim| - expect(claims[key.to_sym]).to eq(claim) - end - end - end - - context 'when encoding as HS256' do - let(:algorithm) { 'HS256' } - let(:key) { 'super_secret!' } - - before do - client_assertion.get_token(claims, algorithm: algorithm, key: key) - raise 'No request made!' if @request_body.nil? - end - - it_behaves_like 'encodes the JWT' - - context 'with real key' do - let(:key) { '1883be842495c3b58f68ca71fbf1397fbb9ed2fdf8990f8404a25d0a1b995943' } - - it_behaves_like 'encodes the JWT' - end - end - - context 'when encoding as RS256' do - let(:algorithm) { 'RS256' } - let(:key) { OpenSSL::PKey::RSA.new(1024) } - - before do - client_assertion.get_token(claims, algorithm: algorithm, key: key) - raise 'No request made!' if @request_body.nil? - end - - it_behaves_like 'encodes the JWT' - - context 'with private key' do - let(:private_key_file) { 'spec/fixtures/RS256/jwtRS256.key' } - let(:password) { '' } - let(:key) { OpenSSL::PKey::RSA.new(File.read(private_key_file), password) } - - it_behaves_like 'encodes the JWT' - end - end - - context 'with bad encoding params' do - let(:encoding_opts) { {algorithm: algorithm, key: key} } - - describe 'non-supported algorithms' do - let(:algorithm) { 'the blockchain' } - let(:key) { 'machine learning' } - - it 'raises NotImplementedError' do - # this behavior is handled by the JWT gem, but this should make sure it is consistent - expect { client_assertion.get_token(claims, encoding_opts) }.to raise_error(NotImplementedError) - end - end - - describe 'of a wrong object type' do - let(:encoding_opts) { 'the cloud' } - - it 'raises ArgumentError' do - expect { client_assertion.get_token(claims, encoding_opts) }.to raise_error(ArgumentError, /encoding_opts/) - end - end - - describe 'missing encoding_opts[:algorithm]' do - before do - encoding_opts.delete(:algorithm) - end - - it 'raises ArgumentError' do - expect { client_assertion.get_token(claims, encoding_opts) }.to raise_error(ArgumentError, /encoding_opts/) - end - end - - describe 'missing encoding_opts[:key]' do - before do - encoding_opts.delete(:key) - end - - it 'raises ArgumentError' do - expect { client_assertion.get_token(claims, encoding_opts) }.to raise_error(ArgumentError, /encoding_opts/) - end - end - end - end - - describe 'POST request parameters' do - context 'when using :auth_scheme => :request_body' do - let(:auth_scheme) { :request_body } - - it 'includes assertion and grant_type, along with the client parameters' do - client_assertion.get_token(claims, algorithm: algorithm, key: key) - expect(@request_body).not_to be_nil - expect(@request_body.keys).to match_array(%i[assertion grant_type client_id client_secret]) - expect(@request_body[:grant_type]).to eq('urn:ietf:params:oauth:grant-type:jwt-bearer') - expect(@request_body[:assertion]).to be_a(String) - expect(@request_body[:client_id]).to eq('abc') - expect(@request_body[:client_secret]).to eq('def') - end - - it 'includes other params via request_options' do - client_assertion.get_token(claims, {algorithm: algorithm, key: key}, {scope: 'dover sole'}) - expect(@request_body).not_to be_nil - expect(@request_body.keys).to match_array(%i[assertion grant_type scope client_id client_secret]) - expect(@request_body[:grant_type]).to eq('urn:ietf:params:oauth:grant-type:jwt-bearer') - expect(@request_body[:assertion]).to be_a(String) - expect(@request_body[:scope]).to eq('dover sole') - expect(@request_body[:client_id]).to eq('abc') - expect(@request_body[:client_secret]).to eq('def') - end - end - - context 'when using :auth_scheme => :basic_auth' do - let(:auth_scheme) { :basic_auth } - - it 'includes assertion and grant_type by default' do - client_assertion.get_token(claims, algorithm: algorithm, key: key) - expect(@request_body).not_to be_nil - expect(@request_body.keys).to match_array(%i[assertion grant_type]) - expect(@request_body[:grant_type]).to eq('urn:ietf:params:oauth:grant-type:jwt-bearer') - expect(@request_body[:assertion]).to be_a(String) - end - - it 'includes other params via request_options' do - client_assertion.get_token(claims, {algorithm: algorithm, key: key}, {scope: 'dover sole'}) - expect(@request_body).not_to be_nil - expect(@request_body.keys).to match_array(%i[assertion grant_type scope]) - expect(@request_body[:grant_type]).to eq('urn:ietf:params:oauth:grant-type:jwt-bearer') - expect(@request_body[:assertion]).to be_a(String) - expect(@request_body[:scope]).to eq('dover sole') - end - end - end - - describe 'returning the response' do - let(:access_token) { client_assertion.get_token(claims, {algorithm: algorithm, key: key}, {}, response_opts) } - let(:response_opts) { {} } - - %w[json formencoded].each do |mode| - context "when the content type is #{mode}" do - before do - @response_format = mode - end - - it 'returns an AccessToken' do - expect(access_token).to be_an(OAuth2::AccessToken) - end - - it 'returns AccessToken with same Client' do - expect(access_token.client).to eq(client) - end - - it 'returns AccessToken with #token' do - expect(access_token.token).to eq('salmon') - end - - it 'returns AccessToken with #expires_in' do - expect(access_token.expires_in).to eq(600) - end - - it 'returns AccessToken with #expires_at' do - expect(access_token.expires_at).not_to be_nil - end - - it 'sets AccessToken#refresh_token to nil' do - expect(access_token.refresh_token).to eq('trout') - end - - context 'with custom response_opts' do - let(:response_opts) { {'custom_token_option' => 'mackerel'} } - - it 'passes them into the token params' do - expect(access_token.params).to eq(response_opts) - end - end - - context 'when no custom opts are passed in' do - let(:response_opts) { {} } - - it 'does not set any params by default' do - expect(access_token.params).to eq({}) - end - end - end - end - end - end -end diff --git a/spec/oauth2/strategy/auth_code_spec.rb b/spec/oauth2/strategy/auth_code_spec.rb deleted file mode 100644 index fcc7be3b..00000000 --- a/spec/oauth2/strategy/auth_code_spec.rb +++ /dev/null @@ -1,154 +0,0 @@ -# encoding: utf-8 -# frozen_string_literal: true - -RSpec.describe OAuth2::Strategy::AuthCode do - subject { client.auth_code } - - let(:code) { 'sushi' } - let(:kvform_token) { 'expires_in=600&access_token=salmon&refresh_token=trout&extra_param=steve' } - let(:facebook_token) { kvform_token.gsub('_in', '') } - let(:json_token) { JSON.dump(expires_in: 600, access_token: 'salmon', refresh_token: 'trout', extra_param: 'steve') } - let(:redirect_uri) { 'http://example.com/redirect_uri' } - let(:microsoft_token) { 'id_token=i_am_MSFT' } - - let(:client) do - OAuth2::Client.new('abc', 'def', site: 'http://api.example.com') do |builder| - builder.adapter :test do |stub| - stub.get("/oauth/token?client_id=abc&code=#{code}&grant_type=authorization_code") do |_env| - case @mode - when 'formencoded' - [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, kvform_token] - when 'json' - [200, {'Content-Type' => 'application/json'}, json_token] - when 'from_facebook' - [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, facebook_token] - when 'from_microsoft' - [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, microsoft_token] - else raise ArgumentError, "Bad @mode: #{@mode}" - end - end - stub.post('/oauth/token', 'client_id' => 'abc', 'client_secret' => 'def', 'code' => 'sushi', 'grant_type' => 'authorization_code') do |_env| - case @mode - when 'formencoded' - [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, kvform_token] - when 'json' - [200, {'Content-Type' => 'application/json'}, json_token] - when 'from_facebook' - [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, facebook_token] - else raise ArgumentError, "Bad @mode: #{@mode}" - end - end - stub.post('/oauth/token', 'client_id' => 'abc', 'client_secret' => 'def', 'code' => 'sushi', 'grant_type' => 'authorization_code', 'redirect_uri' => redirect_uri) do |_env| - [200, {'Content-Type' => 'application/json'}, json_token] - end - end - end - end - - describe '#authorize_url' do - it 'includes the client_id' do - expect(subject.authorize_url).to include('client_id=abc') - end - - it 'includes the type' do - expect(subject.authorize_url).to include('response_type=code') - end - - it 'does not include the client_secret' do - expect(subject.authorize_url).not_to include('client_secret=def') - end - - it 'raises an error if the client_secret is passed in' do - expect { subject.authorize_url(client_secret: 'def') }.to raise_error(ArgumentError) - end - - it 'raises an error if the client_secret is passed in with string keys' do - expect { subject.authorize_url('client_secret' => 'def') }.to raise_error(ArgumentError) - end - - it 'includes passed in options' do - cb = 'http://myserver.local/oauth/callback' - expect(subject.authorize_url(redirect_uri: cb)).to include("redirect_uri=#{CGI.escape(cb)}") - end - end - - describe '#get_token (with dynamic redirect_uri)' do - before do - @mode = 'json' - client.options[:token_method] = :post - client.options[:auth_scheme] = :request_body - client.options[:redirect_uri] = redirect_uri - end - - it 'includes redirect_uri once in the request parameters' do - expect { subject.get_token(code, redirect_uri: redirect_uri) }.not_to raise_error - end - end - - describe '#get_token (handling utf-8 data)' do - let(:json_token) { JSON.dump(expires_in: 600, access_token: 'salmon', refresh_token: 'trout', extra_param: 'André') } - - before do - @mode = 'json' - client.options[:token_method] = :post - client.options[:auth_scheme] = :request_body - end - - it 'does not raise an error' do - expect { subject.get_token(code) }.not_to raise_error - end - - it 'does not create an error instance' do - expect(OAuth2::Error).not_to receive(:new) - - subject.get_token(code) - end - end - - describe '#get_token' do - it "doesn't treat an OpenID Connect token with only an id_token (like from Microsoft) as invalid" do - @mode = 'from_microsoft' - client.options[:token_method] = :get - client.options[:auth_scheme] = :request_body - @access = subject.get_token(code) - expect(@access.token).to eq('i_am_MSFT') - end - end - - %w[json formencoded from_facebook].each do |mode| - %i[get post].each do |verb| - describe "#get_token (#{mode}, access_token_method=#{verb}" do - before do - @mode = mode - client.options[:token_method] = verb - client.options[:auth_scheme] = :request_body - @access = subject.get_token(code) - end - - it 'returns AccessToken with same Client' do - expect(@access.client).to eq(client) - end - - it 'returns AccessToken with #token' do - expect(@access.token).to eq('salmon') - end - - it 'returns AccessToken with #refresh_token' do - expect(@access.refresh_token).to eq('trout') - end - - it 'returns AccessToken with #expires_in' do - expect(@access.expires_in).to eq(600) - end - - it 'returns AccessToken with #expires_at' do - expect(@access.expires_at).to be_kind_of(Integer) - end - - it 'returns AccessToken with params accessible via []' do - expect(@access['extra_param']).to eq('steve') - end - end - end - end -end diff --git a/spec/oauth2/strategy/base_spec.rb b/spec/oauth2/strategy/base_spec.rb deleted file mode 100644 index 33b98389..00000000 --- a/spec/oauth2/strategy/base_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe OAuth2::Strategy::Base do - it 'initializes with a Client' do - expect { described_class.new(OAuth2::Client.new('abc', 'def')) }.not_to raise_error - end -end diff --git a/spec/oauth2/strategy/client_credentials_spec.rb b/spec/oauth2/strategy/client_credentials_spec.rb deleted file mode 100644 index b9480098..00000000 --- a/spec/oauth2/strategy/client_credentials_spec.rb +++ /dev/null @@ -1,97 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe OAuth2::Strategy::ClientCredentials do - subject { client.client_credentials } - - let(:kvform_token) { 'expires_in=600&access_token=salmon&refresh_token=trout' } - let(:json_token) { '{"expires_in":600,"access_token":"salmon","refresh_token":"trout"}' } - - let(:client) do - OAuth2::Client.new('abc', 'def', site: 'http://api.example.com') do |builder| - builder.adapter :test do |stub| - stub.post('/oauth/token', 'grant_type' => 'client_credentials') do |env| - client_id, client_secret = Base64.decode64(env[:request_headers]['Authorization'].split(' ', 2)[1]).split(':', 2) - (client_id == 'abc' && client_secret == 'def') || raise(Faraday::Adapter::Test::Stubs::NotFound) - @last_headers = env[:request_headers] - case @mode - when 'formencoded' - [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, kvform_token] - when 'json' - [200, {'Content-Type' => 'application/json'}, json_token] - else raise ArgumentError, "Bad @mode: #{@mode}" - end - end - stub.post('/oauth/token', 'client_id' => 'abc', 'client_secret' => 'def', 'grant_type' => 'client_credentials') do |_env| - case @mode - when 'formencoded' - [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, kvform_token] - when 'json' - [200, {'Content-Type' => 'application/json'}, json_token] - else raise ArgumentError, "Bad @mode: #{@mode}" - end - end - end - end - end - - describe '#authorize_url' do - it 'raises NotImplementedError' do - expect { subject.authorize_url }.to raise_error(NotImplementedError) - end - end - - %w[json formencoded].each do |mode| - %i[basic_auth request_body].each do |auth_scheme| - describe "#get_token (#{mode}) (#{auth_scheme})" do - before do - @mode = mode - client.options[:auth_scheme] = auth_scheme - @access = subject.get_token - end - - it 'returns AccessToken with same Client' do - expect(@access.client).to eq(client) - end - - it 'returns AccessToken with #token' do - expect(@access.token).to eq('salmon') - end - - it 'returns AccessToken without #refresh_token' do - expect(@access.refresh_token).to eq('trout') - end - - it 'returns AccessToken with #expires_in' do - expect(@access.expires_in).to eq(600) - end - - it 'returns AccessToken with #expires_at' do - expect(@access.expires_at).not_to be_nil - end - end - end - end - - describe '#get_token (with extra header parameters)' do - before do - @mode = 'json' - @access = subject.get_token(headers: {'X-Extra-Header' => 'wow'}) - end - - it 'sends the header correctly.' do - expect(@last_headers['X-Extra-Header']).to eq('wow') - end - end - - describe '#get_token (with option overriding response)' do - before do - @mode = 'json' - @access = subject.get_token({}, {'refresh_token' => 'guppy'}) - end - - it 'override is applied' do - expect(@access.token).to eq('salmon') - expect(@access.refresh_token).to eq('guppy') - end - end -end diff --git a/spec/oauth2/strategy/implicit_spec.rb b/spec/oauth2/strategy/implicit_spec.rb deleted file mode 100644 index 18588fea..00000000 --- a/spec/oauth2/strategy/implicit_spec.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe OAuth2::Strategy::Implicit do - subject { client.implicit } - - let(:client) { OAuth2::Client.new('abc', 'def', site: 'http://api.example.com') } - - describe '#authorize_url' do - it 'includes the client_id' do - expect(subject.authorize_url).to include('client_id=abc') - end - - it 'includes the type' do - expect(subject.authorize_url).to include('response_type=token') - end - - it 'does not include the client_secret' do - expect(subject.authorize_url).not_to include('client_secret=def') - end - - it 'raises an error if the client_secret is passed in' do - expect { subject.authorize_url(client_secret: 'def') }.to raise_error(ArgumentError) - end - - it 'raises an error if the client_secret is passed in with string keys' do - expect { subject.authorize_url('client_secret' => 'def') }.to raise_error(ArgumentError) - end - - it 'includes passed in options' do - cb = 'http://myserver.local/oauth/callback' - expect(subject.authorize_url(redirect_uri: cb)).to include("redirect_uri=#{CGI.escape(cb)}") - end - end - - describe '#get_token' do - it 'raises NotImplementedError' do - expect { subject.get_token }.to raise_error(NotImplementedError) - end - end -end diff --git a/spec/oauth2/strategy/password_spec.rb b/spec/oauth2/strategy/password_spec.rb deleted file mode 100644 index 040b6455..00000000 --- a/spec/oauth2/strategy/password_spec.rb +++ /dev/null @@ -1,59 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe OAuth2::Strategy::Password do - subject { client.password } - - let(:client) do - cli = OAuth2::Client.new('abc', 'def', site: 'http://api.example.com') - cli.connection = Faraday.new(cli.site, cli.options[:connection_opts]) do |b| - b.request :url_encoded - b.adapter :test do |stub| - stub.post('/oauth/token') do |_env| - case @mode - when 'formencoded' - [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, 'expires_in=600&access_token=salmon&refresh_token=trout'] - when 'json' - [200, {'Content-Type' => 'application/json'}, '{"expires_in":600,"access_token":"salmon","refresh_token":"trout"}'] - else raise ArgumentError, "Bad @mode: #{@mode}" - end - end - end - end - cli - end - - describe '#authorize_url' do - it 'raises NotImplementedError' do - expect { subject.authorize_url }.to raise_error(NotImplementedError) - end - end - - %w[json formencoded].each do |mode| - describe "#get_token (#{mode})" do - before do - @mode = mode - @access = subject.get_token('username', 'password') - end - - it 'returns AccessToken with same Client' do - expect(@access.client).to eq(client) - end - - it 'returns AccessToken with #token' do - expect(@access.token).to eq('salmon') - end - - it 'returns AccessToken with #refresh_token' do - expect(@access.refresh_token).to eq('trout') - end - - it 'returns AccessToken with #expires_in' do - expect(@access.expires_in).to eq(600) - end - - it 'returns AccessToken with #expires_at' do - expect(@access.expires_at).not_to be_nil - end - end - end -end diff --git a/spec/oauth2/version_spec.rb b/spec/oauth2/version_spec.rb deleted file mode 100644 index 250a3039..00000000 --- a/spec/oauth2/version_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe OAuth2::Version do - it 'has a version number' do - expect(described_class).not_to be_nil - end - - it 'can be a string' do - expect(described_class.to_s).to be_a(String) - end - - it 'allows Constant access' do - expect(described_class::VERSION).to be_a(String) - end - - it 'is greater than 0.1.0' do - expect(Gem::Version.new(described_class)> Gem::Version.new('0.1.0')).to be(true) - end - - it 'major version is an integer' do - expect(described_class.major).to be_a(Integer) - end - - it 'minor version is an integer' do - expect(described_class.minor).to be_a(Integer) - end - - it 'patch version is an integer' do - expect(described_class.patch).to be_a(Integer) - end - - it 'returns a Hash' do - expect(described_class.to_h.keys).to match_array(%i[major minor patch pre]) - end - - it 'returns an Array' do - expect(described_class.to_a).to be_a(Array) - end -end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb deleted file mode 100644 index a770c36d..00000000 --- a/spec/spec_helper.rb +++ /dev/null @@ -1,71 +0,0 @@ -# frozen_string_literal: true - -# ensure test env -ENV['RACK_ENV'] = 'test' - -# Third Party Libraries -require 'rspec' -require 'rspec/stubbed_env' -require 'silent_stream' -require 'addressable/uri' -require 'rspec/pending_for' -require 'rspec/block_is_expected' - -# Extensions -require 'ext/backports' - -DEBUG = ENV['DEBUG'] == 'true' - -ruby_version = Gem::Version.new(RUBY_VERSION) -minimum_version = ->(version, engine = 'ruby') { ruby_version>= Gem::Version.new(version) && RUBY_ENGINE == engine } -actual_version = lambda do |major, minor| - actual = Gem::Version.new(ruby_version) - major == actual.segments[0] && minor == actual.segments[1] && RUBY_ENGINE == 'ruby' -end -debugging = minimum_version.call('2.7') && DEBUG -RUN_COVERAGE = minimum_version.call('2.6') && (ENV['COVER_ALL'] || ENV['CI_CODECOV'] || ENV['CI'].nil?) -ALL_FORMATTERS = actual_version.call(2, 7) && (ENV['COVER_ALL'] || ENV['CI_CODECOV'] || ENV['CI']) - -if DEBUG - if debugging - require 'byebug' - elsif minimum_version.call('2.7', 'jruby') - require 'pry-debugger-jruby' - end -end - -if RUN_COVERAGE - require 'simplecov' # Config file `.simplecov` is run immediately when simplecov loads - require 'codecov' - require 'simplecov-json' - require 'simplecov-lcov' - require 'simplecov-cobertura' - # This will override the formatter set in .simplecov - if ALL_FORMATTERS - SimpleCov::Formatter::LcovFormatter.config do |c| - c.report_with_single_file = true - c.single_report_path = 'coverage/lcov.info' - end - - SimpleCov.formatters = [ - SimpleCov::Formatter::HTMLFormatter, - SimpleCov::Formatter::CoberturaFormatter, # XML for Jenkins - SimpleCov::Formatter::LcovFormatter, - SimpleCov::Formatter::JSONFormatter, # For CodeClimate - SimpleCov::Formatter::Codecov, # For CodeCov - ] - end -end - -# This gem -require 'oauth2' - -# Library Configs -require 'config/multi_xml' -require 'config/faraday' - -# RSpec Configs -require 'config/rspec/rspec_core' -require 'config/rspec/silent_stream' - -VERBS = %i[get post put delete patch].freeze From f336a5b809fda548cd87d1c64c9b7e1c7908c816 Mon Sep 17 00:00:00 2001 From: Peter Boling Date: 2022年9月15日 07:08:23 +0700 Subject: [PATCH 5/7] =?UTF-8?q?=F0=9F=94=A7=20Complete=20migration=20from?= =?UTF-8?q?=20master=20to=20main=20branch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 80766325..7566abdc 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@

- OAuth 2.0 Logo by Chris Messina, CC BY-SA 3.0 + OAuth 2.0 Logo by Chris Messina, CC BY-SA 3.0 - Yukihiro Matsumoto, Ruby Visual Identity Team, CC BY-SA 2.5 + Yukihiro Matsumoto, Ruby Visual Identity Team, CC BY-SA 2.5

From 8787e52b627726f719b68435ab5cae3ef90507d3 Mon Sep 17 00:00:00 2001 From: Peter Boling Date: 2022年9月15日 07:10:11 +0700 Subject: [PATCH 6/7] =?UTF-8?q?=F0=9F=94=A7=20Complete=20migration=20from?= =?UTF-8?q?=20master=20to=20main=20branch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 317 ------------------------------------------------ CONTRIBUTING.md | 2 +- 2 files changed, 1 insertion(+), 318 deletions(-) delete mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 2eaa2e89..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,317 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format (since v2) is based on [Keep a Changelog v1](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning v2](https://semver.org/spec/v2.0.0.html). - -## Unreleased -### Added -### Changed -### Fixed -### Removed - -## [2.0.7] - 2022年08月22日 -### Added -- [#629](https://github.com/oauth-xx/oauth2/pull/629) - Allow POST of JSON to get token (@pboling, @terracatta) - -### Fixed -- [#626](https://github.com/oauth-xx/oauth2/pull/626) - Fixes a regression in 2.0.6. Will now prefer the key order from the lookup, not the hash keys (@rickselby) - - Note: This fixes compatibility with `omniauth-oauth2` and AWS -- [#625](https://github.com/oauth-xx/oauth2/pull/625) - Fixes the printed version in the post install message (@hasghari) - -## [2.0.6] - 2022年07月13日 -### Fixed -- [#624](https://github.com/oauth-xx/oauth2/pull/624) - Fixes a [regression](https://github.com/oauth-xx/oauth2/pull/623) in v2.0.5, where an error would be raised in refresh_token flows due to (legitimate) lack of access_token (@pboling) - -## [2.0.5] - 2022年07月07日 -### Fixed -- [#620](https://github.com/oauth-xx/oauth2/pull/620) - Documentation improvements, to help with upgrading (@swanson) -- [#621](https://github.com/oauth-xx/oauth2/pull/621) - Fixed [#528](https://github.com/oauth-xx/oauth2/issues/528) and [#619](https://github.com/oauth-xx/oauth2/issues/619) (@pboling) - - All data in responses is now returned, with the access token removed and set as `token` - - `refresh_token` is no longer dropped - - **BREAKING**: Microsoft's `id_token` is no longer left as `access_token['id_token']`, but moved to the standard `access_token.token` that all other strategies use - - Remove `parse` and `snaky` from options so they don't get included in response - - There is now 100% test coverage, for lines _and_ branches, and it will stay that way. - -## [2.0.4] - 2022年07月01日 -### Fixed -- [#618](https://github.com/oauth-xx/oauth2/pull/618) - In some scenarios the `snaky` option default value was not applied (@pboling) - -## [2.0.3] - 2022年06月28日 -### Added -- [#611](https://github.com/oauth-xx/oauth2/pull/611) - Proper deprecation warnings for `extract_access_token` argument (@pboling) -- [#612](https://github.com/oauth-xx/oauth2/pull/612) - Add `snaky: false` option to skip conversion to `OAuth2::SnakyHash` (default: true) (@pboling) -### Fixed -- [#608](https://github.com/oauth-xx/oauth2/pull/608) - Wrap `Faraday::TimeoutError` in `OAuth2::TimeoutError` (@nbibler) -- [#615](https://github.com/oauth-xx/oauth2/pull/615) - Fix support for requests with blocks, see `Faraday::Connection#run_request` (@pboling) - -## [2.0.2] - 2022年06月24日 -### Fixed -- [#604](https://github.com/oauth-xx/oauth2/pull/604) - Wrap `Faraday::TimeoutError` in `OAuth2::TimeoutError` (@stanhu) -- [#606](https://github.com/oauth-xx/oauth2/pull/606) - Ruby 2.7 deprecation warning fix: Move `access_token_class` parameter into `Client` constructor (@stanhu) -- [#607](https://github.com/oauth-xx/oauth2/pull/607) - CHANGELOG correction, reference to `OAuth2::ConnectionError` (@zavan) - -## [2.0.1] - 2022年06月22日 -### Added -- Documentation improvements (@pboling) -- Increased test coverage to 99% (@pboling) - -## [2.0.0] - 2022年06月21日 -### Added -- [#158](https://github.com/oauth-xx/oauth2/pull/158), [#344](https://github.com/oauth-xx/oauth2/pull/344) - Optionally pass raw response to parsers (@niels) -- [#190](https://github.com/oauth-xx/oauth2/pull/190), [#332](https://github.com/oauth-xx/oauth2/pull/332), [#334](https://github.com/oauth-xx/oauth2/pull/334), [#335](https://github.com/oauth-xx/oauth2/pull/335), [#360](https://github.com/oauth-xx/oauth2/pull/360), [#426](https://github.com/oauth-xx/oauth2/pull/426), [#427](https://github.com/oauth-xx/oauth2/pull/427), [#461](https://github.com/oauth-xx/oauth2/pull/461) - Documentation (@josephpage, @pboling, @meganemura, @joshRpowell, @elliotcm) -- [#220](https://github.com/oauth-xx/oauth2/pull/220) - Support IETF rfc7523 JWT Bearer Tokens Draft 04+ (@jhmoore) -- [#298](https://github.com/oauth-xx/oauth2/pull/298) - Set the response object on the access token on Client#get_token for debugging (@cpetschnig) -- [#305](https://github.com/oauth-xx/oauth2/pull/305) - Option: `OAuth2::Client#get_token` - `:access_token_class` (`AccessToken`); user specified class to use for all calls to `get_token` (@styd) -- [#346](https://github.com/oauth-xx/oauth2/pull/571) - Modern gem structure (@pboling) -- [#351](https://github.com/oauth-xx/oauth2/pull/351) - Support Jruby 9k (@pboling) -- [#362](https://github.com/oauth-xx/oauth2/pull/362) - Support SemVer release version scheme (@pboling) -- [#363](https://github.com/oauth-xx/oauth2/pull/363) - New method `OAuth2::AccessToken#refresh!` same as old `refresh`, with backwards compatibility alias (@pboling) -- [#364](https://github.com/oauth-xx/oauth2/pull/364) - Support `application/hal+json` format (@pboling) -- [#365](https://github.com/oauth-xx/oauth2/pull/365) - Support `application/vnd.collection+json` format (@pboling) -- [#376](https://github.com/oauth-xx/oauth2/pull/376) - _Documentation_: Example / Test for Google 2-legged JWT (@jhmoore) -- [#381](https://github.com/oauth-xx/oauth2/pull/381) - Spec for extra header params on client credentials (@nikz) -- [#394](https://github.com/oauth-xx/oauth2/pull/394) - Option: `OAuth2::AccessToken#initialize` - `:expires_latency` (`nil`); number of seconds by which AccessToken validity will be reduced to offset latency (@klippx) -- [#412](https://github.com/oauth-xx/oauth2/pull/412) - Support `application/vdn.api+json` format (from jsonapi.org) (@david-christensen) -- [#413](https://github.com/oauth-xx/oauth2/pull/413) - _Documentation_: License scan and report (@meganemura) -- [#442](https://github.com/oauth-xx/oauth2/pull/442) - Option: `OAuth2::Client#initialize` - `:logger` (`::Logger.new($stdout)`) logger to use when OAUTH_DEBUG is enabled (for parity with `1-4-stable` branch) (@rthbound) -- [#494](https://github.com/oauth-xx/oauth2/pull/494) - Support [OIDC 1.0 Private Key JWT](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication); based on the OAuth JWT assertion specification [(RFC 7523)](https://tools.ietf.org/html/rfc7523) (@SteveyblamWork) -- [#549](https://github.com/oauth-xx/oauth2/pull/549) - Wrap `Faraday::ConnectionFailed` in `OAuth2::ConnectionError` (@nikkypx) -- [#550](https://github.com/oauth-xx/oauth2/pull/550) - Raise error if location header not present when redirecting (@stanhu) -- [#552](https://github.com/oauth-xx/oauth2/pull/552) - Add missing `version.rb` require (@ahorek) -- [#553](https://github.com/oauth-xx/oauth2/pull/553) - Support `application/problem+json` format (@janz93) -- [#560](https://github.com/oauth-xx/oauth2/pull/560) - Support IETF rfc6749, section 2.3.1 - don't set auth params when `nil` (@bouk) -- [#571](https://github.com/oauth-xx/oauth2/pull/571) - Support Ruby 3.1 (@pboling) -- [#575](https://github.com/oauth-xx/oauth2/pull/575) - Support IETF rfc7231, section 7.1.2 - relative location in redirect (@pboling) -- [#581](https://github.com/oauth-xx/oauth2/pull/581) - _Documentation_: of breaking changes (@pboling) -### Changed -- [#191](https://github.com/oauth-xx/oauth2/pull/191) - **BREAKING**: Token is expired if `expired_at` time is `now` (@davestevens) -- [#312](https://github.com/oauth-xx/oauth2/pull/312) - **BREAKING**: Set `:basic_auth` as default for `:auth_scheme` instead of `:request_body`. This was default behavior before 1.3.0. (@tetsuya, @wy193777) -- [#317](https://github.com/oauth-xx/oauth2/pull/317) - _Dependency_: Upgrade `jwt` to 2.x.x (@travisofthenorth) -- [#338](https://github.com/oauth-xx/oauth2/pull/338) - _Dependency_: Switch from `Rack::Utils.escape` to `CGI.escape` (@josephpage) -- [#339](https://github.com/oauth-xx/oauth2/pull/339), [#368](https://github.com/oauth-xx/oauth2/pull/368), [#424](https://github.com/oauth-xx/oauth2/pull/424), [#479](https://github.com/oauth-xx/oauth2/pull/479), [#493](https://github.com/oauth-xx/oauth2/pull/493), [#539](https://github.com/oauth-xx/oauth2/pull/539), [#542](https://github.com/oauth-xx/oauth2/pull/542), [#553](https://github.com/oauth-xx/oauth2/pull/553) - CI Updates, code coverage, linting, spelling, type fixes, New VERSION constant (@pboling, @josephpage, @ahorek) -- [#410](https://github.com/oauth-xx/oauth2/pull/410) - **BREAKING**: Removed the ability to call .error from an OAuth2::Response object (@jhmoore) -- [#414](https://github.com/oauth-xx/oauth2/pull/414) - Use Base64.strict_encode64 instead of custom internal logic (@meganemura) -- [#489](https://github.com/oauth-xx/oauth2/pull/489) - **BREAKING**: Default value for option `OAuth2::Client` - `:authorize_url` removed leading slash to work with relative paths by default (`'oauth/authorize'`) (@ghost) -- [#489](https://github.com/oauth-xx/oauth2/pull/489) - **BREAKING**: Default value for option `OAuth2::Client` - `:token_url` removed leading slash to work with relative paths by default (`'oauth/token'`) (@ghost) -- [#507](https://github.com/oauth-xx/oauth2/pull/507), [#575](https://github.com/oauth-xx/oauth2/pull/575) - **BREAKING**: Transform keys to camel case, always, by default (ultimately via `rash_alt` gem) - - Original keys will still work as previously, in most scenarios, thanks to `rash_alt` gem. - - However, this is a _breaking_ change if you rely on `response.parsed.to_h`, as the keys in the result will be camel case. - - As of version 2.0.4 you can turn key transformation off with the `snaky: false` option. -- [#576](https://github.com/oauth-xx/oauth2/pull/576) - **BREAKING**: Stop rescuing parsing errors (@pboling) -- [#591](https://github.com/oauth-xx/oauth2/pull/576) - _DEPRECATION_: `OAuth2::Client` - `:extract_access_token` option is deprecated -### Fixed -- [#158](https://github.com/oauth-xx/oauth2/pull/158), [#344](https://github.com/oauth-xx/oauth2/pull/344) - Handling of errors when using `omniauth-facebook` (@niels) -- [#294](https://github.com/oauth-xx/oauth2/pull/294) - Fix: "Unexpected middleware set" issue with Faraday when `OAUTH_DEBUG=true` (@spectator, @gafrom) -- [#300](https://github.com/oauth-xx/oauth2/pull/300) - _Documentation_: `Oauth2::Error` - Error codes are strings, not symbols (@NobodysNightmare) -- [#318](https://github.com/oauth-xx/oauth2/pull/318), [#326](https://github.com/oauth-xx/oauth2/pull/326), [#343](https://github.com/oauth-xx/oauth2/pull/343), [#347](https://github.com/oauth-xx/oauth2/pull/347), [#397](https://github.com/oauth-xx/oauth2/pull/397), [#464](https://github.com/oauth-xx/oauth2/pull/464), [#561](https://github.com/oauth-xx/oauth2/pull/561), [#565](https://github.com/oauth-xx/oauth2/pull/565) - _Dependency_: Support all versions of `faraday` (see [gemfiles/README.md][gemfiles/readme] for compatibility matrix with Ruby engines & versions) (@pboling, @raimondasv, @zacharywelch, @Fudoshiki, @ryogift, @sj26, @jdelStrother) -- [#322](https://github.com/oauth-xx/oauth2/pull/322), [#331](https://github.com/oauth-xx/oauth2/pull/331), [#337](https://github.com/oauth-xx/oauth2/pull/337), [#361](https://github.com/oauth-xx/oauth2/pull/361), [#371](https://github.com/oauth-xx/oauth2/pull/371), [#377](https://github.com/oauth-xx/oauth2/pull/377), [#383](https://github.com/oauth-xx/oauth2/pull/383), [#392](https://github.com/oauth-xx/oauth2/pull/392), [#395](https://github.com/oauth-xx/oauth2/pull/395), [#400](https://github.com/oauth-xx/oauth2/pull/400), [#401](https://github.com/oauth-xx/oauth2/pull/401), [#403](https://github.com/oauth-xx/oauth2/pull/403), [#415](https://github.com/oauth-xx/oauth2/pull/415), [#567](https://github.com/oauth-xx/oauth2/pull/567) - Updated Rubocop, Rubocop plugins and improved code style (@pboling, @bquorning, @lautis, @spectator) -- [#328](https://github.com/oauth-xx/oauth2/pull/328) - _Documentation_: Homepage URL is SSL (@amatsuda) -- [#339](https://github.com/oauth-xx/oauth2/pull/339), [#479](https://github.com/oauth-xx/oauth2/pull/479) - Update testing infrastructure for all supported Rubies (@pboling and @josephpage) -- [#366](https://github.com/oauth-xx/oauth2/pull/366) - **Security**: Fix logging to `$stdout` of request and response bodies via Faraday's logger and `ENV["OAUTH_DEBUG"] == 'true'` (@pboling) -- [#380](https://github.com/oauth-xx/oauth2/pull/380) - Fix: Stop attempting to encode non-encodable objects in `Oauth2::Error` (@jhmoore) -- [#399](https://github.com/oauth-xx/oauth2/pull/399) - Fix: Stop duplicating `redirect_uri` in `get_token` (@markus) -- [#410](https://github.com/oauth-xx/oauth2/pull/410) - Fix: `SystemStackError` caused by circular reference between Error and Response classes (@jhmoore) -- [#460](https://github.com/oauth-xx/oauth2/pull/460) - Fix: Stop throwing errors when `raise_errors` is set to `false`; analog of [#524](https://github.com/oauth-xx/oauth2/pull/524) for `1-4-stable` branch (@joaolrpaulo) -- [#472](https://github.com/oauth-xx/oauth2/pull/472) - **Security**: Add checks to enforce `client_secret` is *never* passed in authorize_url query params for `implicit` and `auth_code` grant types (@dfockler) -- [#482](https://github.com/oauth-xx/oauth2/pull/482) - _Documentation_: Update last of `intridea` links to `oauth-xx` (@pboling) -- [#536](https://github.com/oauth-xx/oauth2/pull/536) - **Security**: Compatibility with more (and recent) Ruby OpenSSL versions, Github Actions, Rubocop updated, analogous to [#535](https://github.com/oauth-xx/oauth2/pull/535) on `1-4-stable` branch (@pboling) -- [#595](https://github.com/oauth-xx/oauth2/pull/595) - Graceful handling of empty responses from `Client#get_token`, respecting `:raise_errors` config (@stanhu) -- [#596](https://github.com/oauth-xx/oauth2/pull/596) - Consistency between `AccessToken#refresh` and `Client#get_token` named arguments (@stanhu) -- [#598](https://github.com/oauth-xx/oauth2/pull/598) - Fix unparseable data not raised as error in `Client#get_token`, respecting `:raise_errors` config (@stanhu) -### Removed -- [#341](https://github.com/oauth-xx/oauth2/pull/341) - Remove Rdoc & Jeweler related files (@josephpage) -- [#342](https://github.com/oauth-xx/oauth2/pull/342) - **BREAKING**: Dropped support for Ruby 1.8 (@josephpage) -- [#539](https://github.com/oauth-xx/oauth2/pull/539) - Remove reliance on globally included OAuth2 in tests, analog of [#538](https://github.com/oauth-xx/oauth2/pull/538) for 1-4-stable (@anderscarling) -- [#566](https://github.com/oauth-xx/oauth2/pull/566) - _Dependency_: Removed `wwtd` (@bquorning) -- [#589](https://github.com/oauth-xx/oauth2/pull/589), [#593](https://github.com/oauth-xx/oauth2/pull/593) - Remove support for expired MAC token draft spec (@stanhu) -- [#590](https://github.com/oauth-xx/oauth2/pull/590) - _Dependency_: Removed `multi_json` (@stanhu) - -## [1.4.10] - 2022年07月01日 -- FIPS Compatibility [#587](https://github.com/oauth-xx/oauth2/pull/587) (@akostadinov) - -## [1.4.9] - 2022年02月20日 -- Fixes compatibility with Faraday v2 [572](https://github.com/oauth-xx/oauth2/issues/572) -- Includes supported versions of Faraday in test matrix: - - Faraday ~> 2.2.0 with Ruby>= 2.6 - - Faraday ~> 1.10 with Ruby>= 2.4 - - Faraday ~> 0.17.3 with Ruby>= 1.9 -- Add Windows and MacOS to test matrix - -## [1.4.8] - 2022年02月18日 -- MFA is now required to push new gem versions (@pboling) -- README overhaul w/ new Ruby Version and Engine compatibility policies (@pboling) -- [#569](https://github.com/oauth-xx/oauth2/pull/569) Backport fixes ([#561](https://github.com/oauth-xx/oauth2/pull/561) by @ryogift), and add more fixes, to allow faraday 1.x and 2.x (@jrochkind) -- Improve Code Coverage tracking (Coveralls, CodeCov, CodeClimate), and enable branch coverage (@pboling) -- Add CodeQL, Security Policy, Funding info (@pboling) -- Added Ruby 3.1, jruby, jruby-head, truffleruby, truffleruby-head to build matrix (@pboling) -- [#543](https://github.com/oauth-xx/oauth2/pull/543) - Support for more modern Open SSL libraries (@pboling) - -## [1.4.7] - 2021年03月19日 -- [#541](https://github.com/oauth-xx/oauth2/pull/541) - Backport fix to expires_at handling [#533](https://github.com/oauth-xx/oauth2/pull/533) to 1-4-stable branch. (@dobon) - -## [1.4.6] - 2021年03月19日 -- [#540](https://github.com/oauth-xx/oauth2/pull/540) - Add VERSION constant (@pboling) -- [#537](https://github.com/oauth-xx/oauth2/pull/537) - Fix crash in OAuth2::Client#get_token (@anderscarling) -- [#538](https://github.com/oauth-xx/oauth2/pull/538) - Remove reliance on globally included OAuth2 in tests, analogous to [#539](https://github.com/oauth-xx/oauth2/pull/539) on master branch (@anderscarling) - -## [1.4.5] - 2021年03月18日 -- [#535](https://github.com/oauth-xx/oauth2/pull/535) - Compatibility with range of supported Ruby OpenSSL versions, Rubocop updates, Github Actions, analogous to [#536](https://github.com/oauth-xx/oauth2/pull/536) on master branch (@pboling) -- [#518](https://github.com/oauth-xx/oauth2/pull/518) - Add extract_access_token option to OAuth2::Client (@jonspalmer) -- [#507](https://github.com/oauth-xx/oauth2/pull/507) - Fix camel case content type, response keys (@anvox) -- [#500](https://github.com/oauth-xx/oauth2/pull/500) - Fix YARD documentation formatting (@olleolleolle) - -## [1.4.4] - 2020年02月12日 -- [#408](https://github.com/oauth-xx/oauth2/pull/408) - Fixed expires_at for formatted time (@Lomey) - -## [1.4.3] - 2020年01月29日 -- [#483](https://github.com/oauth-xx/oauth2/pull/483) - add project metadata to gemspec (@orien) -- [#495](https://github.com/oauth-xx/oauth2/pull/495) - support additional types of access token requests (@SteveyblamFreeagent, @thomcorley, @dgholz) - - Adds support for private_key_jwt and tls_client_auth -- [#433](https://github.com/oauth-xx/oauth2/pull/433) - allow field names with square brackets and numbers in params (@asm256) - -## [1.4.2] - 2019年10月01日 -- [#478](https://github.com/oauth-xx/oauth2/pull/478) - support latest version of faraday & fix build (@pboling) - - Officially support Ruby 2.6 and truffleruby - -## [1.4.1] - 2018年10月13日 -- [#417](https://github.com/oauth-xx/oauth2/pull/417) - update jwt dependency (@thewoolleyman) -- [#419](https://github.com/oauth-xx/oauth2/pull/419) - remove rubocop dependency (temporary, added back in [#423](https://github.com/oauth-xx/oauth2/pull/423)) (@pboling) -- [#418](https://github.com/oauth-xx/oauth2/pull/418) - update faraday dependency (@pboling) -- [#420](https://github.com/oauth-xx/oauth2/pull/420) - update [oauth2.gemspec](https://github.com/oauth-xx/oauth2/blob/1-4-stable/oauth2.gemspec) (@pboling) -- [#421](https://github.com/oauth-xx/oauth2/pull/421) - fix [CHANGELOG.md](https://github.com/oauth-xx/oauth2/blob/1-4-stable/CHANGELOG.md) for previous releases (@pboling) -- [#422](https://github.com/oauth-xx/oauth2/pull/422) - update [LICENSE](https://github.com/oauth-xx/oauth2/blob/1-4-stable/LICENSE) and [README.md](https://github.com/oauth-xx/oauth2/blob/1-4-stable/README.md) (@pboling) -- [#423](https://github.com/oauth-xx/oauth2/pull/423) - update [builds](https://travis-ci.org/oauth-xx/oauth2/builds), [Rakefile](https://github.com/oauth-xx/oauth2/blob/1-4-stable/Rakefile) (@pboling) - - officially document supported Rubies - * Ruby 1.9.3 - * Ruby 2.0.0 - * Ruby 2.1 - * Ruby 2.2 - * [JRuby 1.7][jruby-1.7] (targets MRI v1.9) - * [JRuby 9.0][jruby-9.0] (targets MRI v2.0) - * Ruby 2.3 - * Ruby 2.4 - * Ruby 2.5 - * [JRuby 9.1][jruby-9.1] (targets MRI v2.3) - * [JRuby 9.2][jruby-9.2] (targets MRI v2.5) - -[jruby-1.7]: https://www.jruby.org/2017/05/11/jruby-1-7-27.html -[jruby-9.0]: https://www.jruby.org/2016/01/26/jruby-9-0-5-0.html -[jruby-9.1]: https://www.jruby.org/2017/05/16/jruby-9-1-9-0.html -[jruby-9.2]: https://www.jruby.org/2018/05/24/jruby-9-2-0-0.html - -## [1.4.0] - 2017年06月09日 -- Drop Ruby 1.8.7 support (@sferik) -- Fix some RuboCop offenses (@sferik) -- _Dependency_: Remove Yardstick (@sferik) -- _Dependency_: Upgrade Faraday to 0.12 (@sferik) - -## [1.3.1] - 2017年03月03日 -- Add support for Ruby 2.4.0 (@pschambacher) -- _Dependency_: Upgrade Faraday to Faraday 0.11 (@mcfiredrill, @rhymes, @pschambacher) - -## [1.3.0] - 2016年12月28日 -- Add support for header-based authentication to the `Client` so it can be used across the library (@bjeanes) -- Default to header-based authentication when getting a token from an authorisation code (@maletor) -- **Breaking**: Allow an `auth_scheme` (`:basic_auth` or `:request_body`) to be set on the client, defaulting to `:request_body` to maintain backwards compatibility (@maletor, @bjeanes) -- Handle `redirect_uri` according to the OAuth 2 spec, so it is passed on redirect and at the point of token exchange (@bjeanes) -- Refactor handling of encoding of error responses (@urkle) -- Avoid instantiating an `Error` if there is no error to raise (@urkle) -- Add support for Faraday 0.10 (@rhymes) - -## [1.2.0] - 2016年07月01日 -- Properly handle encoding of error responses (so we don't blow up, for example, when Google's response includes a ∞) (@Motoshi-Nishihira) -- Make a copy of the options hash in `AccessToken#from_hash` to avoid accidental mutations (@Linuus) -- Use `raise` rather than `fail` to throw exceptions (@sferik) - -## [1.1.0] - 2016年01月30日 -- Various refactors (eliminating `Hash#merge!` usage in `AccessToken#refresh!`, use `yield` instead of `#call`, freezing mutable objects in constants, replacing constants with class variables) (@sferik) -- Add support for Rack 2, and bump various other dependencies (@sferik) - -## [1.0.0] - 2014年07月09日 -### Added -- Add an implementation of the MAC token spec. - -### Fixed -- Fix Base64.strict_encode64 incompatibility with Ruby 1.8.7. -## [0.5.0] - 2011年07月29日 - -### Changed -- [breaking] `oauth_token` renamed to `oauth_bearer`. -- [breaking] `authorize_path` Client option renamed to `authorize_url`. -- [breaking] `access_token_path` Client option renamed to `token_url`. -- [breaking] `access_token_method` Client option renamed to `token_method`. -- [breaking] `web_server` renamed to `auth_code`. - -## [0.4.1] - 2011年04月20日 - -## [0.4.0] - 2011年04月20日 - -## [0.3.0] - 2011年04月08日 - -## [0.2.0] - 2011年04月01日 - -## [0.1.1] - 2011年01月12日 - -## [0.1.0] - 2010年10月13日 - -## [0.0.13] + [0.0.12] + [0.0.11] - 2010年08月17日 - -## [0.0.10] - 2010年06月19日 - -## [0.0.9] - 2010年06月18日 - -## [0.0.8] + [0.0.7] - 2010年04月27日 - -## [0.0.6] - 2010年04月25日 - -## [0.0.5] - 2010年04月23日 - -## [0.0.4] + [0.0.3] + [0.0.2] + [0.0.1] - 2010年04月22日 - -[0.0.1]: https://github.com/oauth-xx/oauth2/compare/311d9f4...v0.0.1 -[0.0.2]: https://github.com/oauth-xx/oauth2/compare/v0.0.1...v0.0.2 -[0.0.3]: https://github.com/oauth-xx/oauth2/compare/v0.0.2...v0.0.3 -[0.0.4]: https://github.com/oauth-xx/oauth2/compare/v0.0.3...v0.0.4 -[0.0.5]: https://github.com/oauth-xx/oauth2/compare/v0.0.4...v0.0.5 -[0.0.6]: https://github.com/oauth-xx/oauth2/compare/v0.0.5...v0.0.6 -[0.0.7]: https://github.com/oauth-xx/oauth2/compare/v0.0.6...v0.0.7 -[0.0.8]: https://github.com/oauth-xx/oauth2/compare/v0.0.7...v0.0.8 -[0.0.9]: https://github.com/oauth-xx/oauth2/compare/v0.0.8...v0.0.9 -[0.0.10]: https://github.com/oauth-xx/oauth2/compare/v0.0.9...v0.0.10 -[0.0.11]: https://github.com/oauth-xx/oauth2/compare/v0.0.10...v0.0.11 -[0.0.12]: https://github.com/oauth-xx/oauth2/compare/v0.0.11...v0.0.12 -[0.0.13]: https://github.com/oauth-xx/oauth2/compare/v0.0.12...v0.0.13 -[0.1.0]: https://github.com/oauth-xx/oauth2/compare/v0.0.13...v0.1.0 -[0.1.1]: https://github.com/oauth-xx/oauth2/compare/v0.1.0...v0.1.1 -[0.2.0]: https://github.com/oauth-xx/oauth2/compare/v0.1.1...v0.2.0 -[0.3.0]: https://github.com/oauth-xx/oauth2/compare/v0.2.0...v0.3.0 -[0.4.0]: https://github.com/oauth-xx/oauth2/compare/v0.3.0...v0.4.0 -[0.4.1]: https://github.com/oauth-xx/oauth2/compare/v0.4.0...v0.4.1 -[0.5.0]: https://github.com/oauth-xx/oauth2/compare/v0.4.1...v0.5.0 -[1.0.0]: https://github.com/oauth-xx/oauth2/compare/v0.9.4...v1.0.0 -[1.1.0]: https://github.com/oauth-xx/oauth2/compare/v1.0.0...v1.1.0 -[1.2.0]: https://github.com/oauth-xx/oauth2/compare/v1.1.0...v1.2.0 -[1.3.0]: https://github.com/oauth-xx/oauth2/compare/v1.2.0...v1.3.0 -[1.3.1]: https://github.com/oauth-xx/oauth2/compare/v1.3.0...v1.3.1 -[1.4.0]: https://github.com/oauth-xx/oauth2/compare/v1.3.1...v1.4.0 -[1.4.1]: https://github.com/oauth-xx/oauth2/compare/v1.4.0...v1.4.1 -[1.4.2]: https://github.com/oauth-xx/oauth2/compare/v1.4.1...v1.4.2 -[1.4.3]: https://github.com/oauth-xx/oauth2/compare/v1.4.2...v1.4.3 -[1.4.4]: https://github.com/oauth-xx/oauth2/compare/v1.4.3...v1.4.4 -[1.4.5]: https://github.com/oauth-xx/oauth2/compare/v1.4.4...v1.4.5 -[1.4.6]: https://github.com/oauth-xx/oauth2/compare/v1.4.5...v1.4.6 -[1.4.7]: https://github.com/oauth-xx/oauth2/compare/v1.4.6...v1.4.7 -[1.4.8]: https://github.com/oauth-xx/oauth2/compare/v1.4.7...v1.4.8 -[1.4.9]: https://github.com/oauth-xx/oauth2/compare/v1.4.8...v1.4.9 -[1.4.10]: https://github.com/oauth-xx/oauth2/compare/v1.4.9...v1.4.10 -[2.0.0]: https://github.com/oauth-xx/oauth2/compare/v1.4.10...v2.0.0 -[2.0.1]: https://github.com/oauth-xx/oauth2/compare/v2.0.0...v2.0.1 -[2.0.2]: https://github.com/oauth-xx/oauth2/compare/v2.0.1...v2.0.2 -[2.0.3]: https://github.com/oauth-xx/oauth2/compare/v2.0.2...v2.0.3 -[2.0.4]: https://github.com/oauth-xx/oauth2/compare/v2.0.3...v2.0.4 -[2.0.5]: https://github.com/oauth-xx/oauth2/compare/v2.0.4...v2.0.5 -[2.0.6]: https://github.com/oauth-xx/oauth2/compare/v2.0.5...v2.0.6 -[2.0.7]: https://github.com/oauth-xx/oauth2/compare/v2.0.6...v2.0.7 -[Unreleased]: https://github.com/oauth-xx/oauth2/compare/v2.0.7...HEAD -[gemfiles/readme]: gemfiles/README.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c11ff521..ed315d0c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -33,7 +33,7 @@ Made with [contributors-img][contrib-rocks]. [comment]: (Following links are used by README, CONTRIBUTING) -[conduct]: https://github.com/oauth-xx/oauth2/blob/master/CODE_OF_CONDUCT.md +[conduct]: https://github.com/oauth-xx/oauth2/blob/main/CODE_OF_CONDUCT.md [contrib-rocks]: https://contrib.rocks From 1ae029a963ffa4d0e5daeea83206c38a5b883f0c Mon Sep 17 00:00:00 2001 From: Peter Boling Date: 2022年9月16日 11:03:13 +0700 Subject: [PATCH 7/7] =?UTF-8?q?=F0=9F=94=A7=20Migration=20from=20Github=20?= =?UTF-8?q?to=20Gitlab?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CONTRIBUTING.md | 8 ++++---- LICENSE | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ed315d0c..e478c5e5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ ## Contributing -Bug reports and pull requests are welcome on GitHub at [https://github.com/oauth-xx/oauth2][source] +Bug reports and pull requests are welcome on GitHub at [https://gitlab.com/oauth-xx/oauth2][source] . This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct][conduct]. @@ -33,12 +33,12 @@ Made with [contributors-img][contrib-rocks]. [comment]: (Following links are used by README, CONTRIBUTING) -[conduct]: https://github.com/oauth-xx/oauth2/blob/main/CODE_OF_CONDUCT.md +[conduct]: https://gitlab.com/oauth-xx/oauth2/-/blob/main/CODE_OF_CONDUCT.md [contrib-rocks]: https://contrib.rocks -[contributors]: https://github.com/oauth-xx/oauth2/graphs/contributors +[contributors]: https://gitlab.com/oauth-xx/oauth2/-/graphs/main [comment]: (Following links are used by README, CONTRIBUTING, Homepage) -[source]: https://github.com/oauth-xx/oauth2/ +[source]: https://gitlab.com/oauth-xx/oauth2/ diff --git a/LICENSE b/LICENSE index 0d3a82cd..5fd4bd3c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ MIT License Copyright (c) 2011 - 2013 Michael Bleigh and Intridea, Inc. -Copyright (c) 2017 - 2022 oauth-xx organization, https://github.com/oauth-xx +Copyright (c) 2017 - 2022 oauth-xx organization, https://gitlab.com/oauth-xx Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal

AltStyle によって変換されたページ (->オリジナル) /