├── .gitignore ├── .rspec ├── .rubocop.yml ├── .rubocop_todo.yml ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── UPGRADING.md ├── lib ├── generators │ ├── templates │ │ └── wine_bouncer.rb │ └── wine_bouncer │ │ └── initializer_generator.rb ├── wine_bouncer.rb └── wine_bouncer │ ├── auth_methods │ └── auth_methods.rb │ ├── auth_strategies │ ├── default.rb │ ├── protected.rb │ └── swagger.rb │ ├── base_strategy.rb │ ├── configuration.rb │ ├── errors.rb │ ├── extension.rb │ ├── oauth2.rb │ └── version.rb ├── spec ├── dummy │ ├── README.rdoc │ ├── Rakefile │ ├── app │ │ ├── api │ │ │ ├── default_api.rb │ │ │ ├── protected_api.rb │ │ │ └── swagger_api.rb │ │ ├── assets │ │ │ ├── config │ │ │ │ └── manifest.js │ │ │ ├── images │ │ │ │ └── .keep │ │ │ ├── javascripts │ │ │ │ └── application.js │ │ │ └── stylesheets │ │ │ │ └── application.css │ │ ├── controllers │ │ │ ├── application_controller.rb │ │ │ └── concerns │ │ │ │ └── .keep │ │ ├── helpers │ │ │ └── application_helper.rb │ │ ├── mailers │ │ │ └── .keep │ │ ├── models │ │ │ ├── .keep │ │ │ ├── concerns │ │ │ │ └── .keep │ │ │ └── user.rb │ │ └── views │ │ │ └── layouts │ │ │ └── application.html.erb │ ├── bin │ │ ├── bundle │ │ ├── rails │ │ └── rake │ ├── config.ru │ ├── config │ │ ├── application.rb │ │ ├── boot.rb │ │ ├── database.yml │ │ ├── environment.rb │ │ ├── environments │ │ │ ├── development.rb │ │ │ ├── production.rb │ │ │ └── test.rb │ │ ├── initializers │ │ │ ├── assets.rb │ │ │ ├── backtrace_silencers.rb │ │ │ ├── cookies_serializer.rb │ │ │ ├── doorkeeper.rb │ │ │ ├── filter_parameter_logging.rb │ │ │ ├── inflections.rb │ │ │ ├── mime_types.rb │ │ │ ├── secret_token.rb │ │ │ ├── session_store.rb │ │ │ └── wrap_parameters.rb │ │ ├── locales │ │ │ ├── doorkeeper.en.yml │ │ │ └── en.yml │ │ ├── routes.rb │ │ └── secrets.yml │ ├── db │ │ ├── migrate │ │ │ ├── 20140915153344_create_users.rb │ │ │ └── 20140915160601_create_doorkeeper_tables.rb │ │ └── schema.rb │ ├── lib │ │ └── assets │ │ │ └── .keep │ ├── log │ │ └── .keep │ └── public │ │ ├── 404.html │ │ ├── 422.html │ │ ├── 500.html │ │ └── favicon.ico ├── factories │ ├── access_token.rb │ ├── application.rb │ └── user.rb ├── intergration │ ├── oauth2_default_strategy_spec.rb │ ├── oauth2_protected_strategy_spec.rb │ └── oauth2_swagger_strategy_spec.rb ├── lib │ ├── generators │ │ └── wine_bouncer │ │ │ └── initializer_generator_spec.rb │ └── wine_bouncer │ │ ├── auth_methods │ │ └── auth_methods_spec.rb │ │ └── auth_strategies │ │ ├── default_spec.rb │ │ └── swagger_spec.rb ├── rails_helper.rb ├── shared │ └── orm │ │ └── active_record.rb └── spec_helper.rb └── wine_bouncer.gemspec /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | /.idea/ 11 | *.bundle 12 | *.so 13 | *.o 14 | *.a 15 | mkmf.log 16 | .rvmrc 17 | .DS_Store 18 | log/*.log 19 | spec/dummy/db/*.sqlite3 20 | spec/dummy/log/*.log 21 | spec/dummy/tmp/ -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | # This is the configuration used to check the rubocop source code. 2 | 3 | inherit_from: .rubocop_todo.yml 4 | 5 | AllCops: 6 | Exclude: 7 | - 'vendor/**/*' 8 | - 'spec/fixtures/**/*' 9 | - 'spec/dummy/**/*' 10 | 11 | Metrics/BlockLength: 12 | Exclude: 13 | - spec/**/* 14 | 15 | LineLength: 16 | Exclude: 17 | - spec/**/* 18 | 19 | StringLiterals: 20 | Enabled: false 21 | 22 | TrailingBlankLines: 23 | Enabled: true -------------------------------------------------------------------------------- /.rubocop_todo.yml: -------------------------------------------------------------------------------- 1 | # This configuration was generated by 2 | # `rubocop --auto-gen-config` 3 | # on 2016-01-07 10:02:02 +0100 using RuboCop version 0.35.1. 4 | # The point is for the user to remove these configuration records 5 | # one by one as the offenses are removed from the code base. 6 | # Note that changes in the inspected code, or installation of new 7 | # versions of RuboCop, may require this file to be generated again. 8 | 9 | # Offense count: 2 10 | Metrics/AbcSize: 11 | Max: 29 12 | 13 | # Offense count: 164 14 | # Configuration parameters: AllowURI, URISchemes. 15 | Metrics/LineLength: 16 | Max: 198 17 | 18 | # Offense count: 1 19 | # Configuration parameters: CountComments. 20 | Metrics/MethodLength: 21 | Max: 32 22 | 23 | # Offense count: 1 24 | Style/AccessorMethodName: 25 | Exclude: 26 | - 'lib/wine_bouncer/oauth2.rb' 27 | 28 | # Offense count: 1 29 | # Cop supports --auto-correct. 30 | Style/BlockComments: 31 | Exclude: 32 | - 'spec/spec_helper.rb' 33 | 34 | # Offense count: 1 35 | # Cop supports --auto-correct. 36 | Style/CommentIndentation: 37 | Exclude: 38 | - 'spec/rails_helper.rb' 39 | 40 | # Offense count: 14 41 | # Configuration parameters: Exclude. 42 | Style/Documentation: 43 | Exclude: 44 | - 'spec/**/*' 45 | - 'test/**/*' 46 | - 'lib/generators/wine_bouncer/initializer_generator.rb' 47 | - 'lib/wine_bouncer.rb' 48 | - 'lib/wine_bouncer/auth_methods/auth_methods.rb' 49 | - 'lib/wine_bouncer/auth_strategies/default.rb' 50 | - 'lib/wine_bouncer/auth_strategies/protected.rb' 51 | - 'lib/wine_bouncer/auth_strategies/swagger.rb' 52 | - 'lib/wine_bouncer/base_strategy.rb' 53 | - 'lib/wine_bouncer/configuration.rb' 54 | - 'lib/wine_bouncer/errors.rb' 55 | - 'lib/wine_bouncer/extension.rb' 56 | - 'lib/wine_bouncer/oauth2.rb' 57 | - 'lib/wine_bouncer/version.rb' 58 | 59 | # Offense count: 6 60 | Style/DoubleNegation: 61 | Exclude: 62 | - 'lib/wine_bouncer/auth_methods/auth_methods.rb' 63 | - 'lib/wine_bouncer/auth_strategies/default.rb' 64 | - 'lib/wine_bouncer/auth_strategies/protected.rb' 65 | - 'lib/wine_bouncer/auth_strategies/swagger.rb' 66 | 67 | # Offense count: 4 68 | # Cop supports --auto-correct. 69 | # Configuration parameters: EnforcedStyle, SupportedStyles. 70 | Style/EmptyLinesAroundModuleBody: 71 | Exclude: 72 | - 'lib/wine_bouncer/configuration.rb' 73 | - 'spec/dummy/app/api/default_api.rb' 74 | - 'spec/dummy/app/api/protected_api.rb' 75 | - 'spec/dummy/app/api/swagger_api.rb' 76 | 77 | # Offense count: 14 78 | # Cop supports --auto-correct. 79 | # Configuration parameters: AllowForAlignment. 80 | Style/ExtraSpacing: 81 | Exclude: 82 | - 'spec/dummy/bin/rails' 83 | - 'spec/dummy/config.ru' 84 | - 'spec/dummy/db/migrate/20140915160601_create_doorkeeper_tables.rb' 85 | - 'spec/dummy/db/schema.rb' 86 | 87 | # Offense count: 1 88 | # Configuration parameters: MinBodyLength. 89 | Style/GuardClause: 90 | Exclude: 91 | - 'lib/wine_bouncer/oauth2.rb' 92 | 93 | # Offense count: 3 94 | # Cop supports --auto-correct. 95 | # Configuration parameters: Width. 96 | Style/IndentationWidth: 97 | Exclude: 98 | - 'lib/wine_bouncer/auth_methods/auth_methods.rb' 99 | - 'lib/wine_bouncer/auth_strategies/default.rb' 100 | - 'lib/wine_bouncer/auth_strategies/swagger.rb' 101 | 102 | # Offense count: 7 103 | # Cop supports --auto-correct. 104 | Style/LeadingCommentSpace: 105 | Exclude: 106 | - 'lib/wine_bouncer/oauth2.rb' 107 | - 'spec/dummy/config/environments/test.rb' 108 | - 'spec/intergration/oauth2_default_strategy_spec.rb' 109 | - 'spec/intergration/oauth2_protected_strategy_spec.rb' 110 | - 'spec/intergration/oauth2_swagger_strategy_spec.rb' 111 | - 'spec/rails_helper.rb' 112 | 113 | # Offense count: 21 114 | # Cop supports --auto-correct. 115 | Style/MethodCallWithoutArgsParentheses: 116 | Exclude: 117 | - 'spec/lib/wine_bouncer/auth_strategies/default_spec.rb' 118 | - 'spec/lib/wine_bouncer/auth_strategies/swagger_spec.rb' 119 | 120 | # Offense count: 1 121 | # Cop supports --auto-correct. 122 | # Configuration parameters: EnforcedStyle, SupportedStyles. 123 | Style/MethodDefParentheses: 124 | Enabled: false 125 | 126 | # Offense count: 2 127 | # Cop supports --auto-correct. 128 | # Configuration parameters: EnforcedStyle, SupportedStyles. 129 | Style/MultilineOperationIndentation: 130 | Enabled: false 131 | 132 | # Offense count: 1 133 | # Cop supports --auto-correct. 134 | Style/NumericLiterals: 135 | MinDigits: 15 136 | 137 | # Offense count: 1 138 | # Cop supports --auto-correct. 139 | # Configuration parameters: PreferredDelimiters. 140 | Style/PercentLiteralDelimiters: 141 | Exclude: 142 | - 'wine_bouncer.gemspec' 143 | 144 | # Offense count: 7 145 | # Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist. 146 | Style/PredicateName: 147 | Exclude: 148 | - 'lib/wine_bouncer/auth_methods/auth_methods.rb' 149 | - 'lib/wine_bouncer/auth_strategies/default.rb' 150 | - 'lib/wine_bouncer/auth_strategies/protected.rb' 151 | - 'lib/wine_bouncer/auth_strategies/swagger.rb' 152 | 153 | # Offense count: 1 154 | # Configuration parameters: EnforcedStyle, SupportedStyles. 155 | Style/RaiseArgs: 156 | Enabled: false 157 | 158 | # Offense count: 2 159 | # Cop supports --auto-correct. 160 | # Configuration parameters: EnforcedStyle, SupportedStyles. 161 | Style/SignalException: 162 | Exclude: 163 | - 'lib/wine_bouncer/oauth2.rb' 164 | 165 | # Offense count: 1 166 | # Cop supports --auto-correct. 167 | Style/SpaceAfterComma: 168 | Exclude: 169 | - 'spec/lib/wine_bouncer/auth_strategies/swagger_spec.rb' 170 | 171 | # Offense count: 1 172 | # Cop supports --auto-correct. 173 | # Configuration parameters: IgnoredMethods. 174 | Style/SymbolProc: 175 | Exclude: 176 | - 'lib/wine_bouncer/extension.rb' 177 | 178 | # Offense count: 1 179 | # Cop supports --auto-correct. 180 | Style/UnneededPercentQ: 181 | Exclude: 182 | - 'wine_bouncer.gemspec' 183 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: ruby 3 | 4 | before_install: 5 | - gem uninstall -v '>= 2' -i $(rvm gemdir)@global -ax bundler || true 6 | - gem install bundler -v '< 2' 7 | - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter 8 | - chmod +x ./cc-test-reporter 9 | 10 | 11 | install: 12 | - bundle install --without production --path=${BUNDLE_PATH:-vendor/bundle} # Install ruby gems, excluding production only gems such as unicorn (already present by default in Travis) 13 | 14 | cache: 15 | - bundler: true 16 | 17 | rvm: 18 | - 2.4.5 19 | - 2.5.3 20 | - 2.6.0 21 | 22 | env: 23 | global: 24 | CC_TEST_REPORTER_ID=ab1b6ce5f973da033f80ae2e99fadbb32b2f9c37892703956d8ef954c8e8134e 25 | jobs: 26 | - rails="~> 5.1.6.1" grape=1.0.3 doorkeeper=4.4.3 27 | - rails="~> 5.1.6.1" grape=1.0.3 doorkeeper=5.0.2 28 | - rails="~> 5.1.6.1" grape=1.0.3 doorkeeper=5.1.0 29 | - rails="~> 5.1.6.1" grape=1.0.3 doorkeeper=5.2.3 30 | - rails="~> 5.1.6.1" grape=1.1.0 doorkeeper=4.4.3 31 | - rails="~> 5.1.6.1" grape=1.1.0 doorkeeper=5.0.2 32 | - rails="~> 5.1.6.1" grape=1.1.0 doorkeeper=5.1.0 33 | - rails="~> 5.1.6.1" grape=1.1.0 doorkeeper=5.2.3 34 | - rails="~> 5.1.6.1" grape=1.2.5 doorkeeper=4.4.3 35 | - rails="~> 5.1.6.1" grape=1.2.5 doorkeeper=5.0.2 36 | - rails="~> 5.1.6.1" grape=1.2.5 doorkeeper=5.1.0 37 | - rails="~> 5.1.6.1" grape=1.2.5 doorkeeper=5.2.3 38 | - rails="~> 5.1.6.1" grape=1.3.0 doorkeeper=4.4.3 39 | - rails="~> 5.1.6.1" grape=1.3.0 doorkeeper=5.0.2 40 | - rails="~> 5.1.6.1" grape=1.3.0 doorkeeper=5.1.0 41 | - rails="~> 5.1.6.1" grape=1.3.0 doorkeeper=5.2.3 42 | - rails="~> 5.2.4.1" grape=1.0.3 doorkeeper=4.4.3 43 | - rails="~> 5.2.4.1" grape=1.0.3 doorkeeper=5.0.2 44 | - rails="~> 5.2.4.1" grape=1.0.3 doorkeeper=5.1.0 45 | - rails="~> 5.2.4.1" grape=1.0.3 doorkeeper=5.2.3 46 | - rails="~> 5.2.4.1" grape=1.1.0 doorkeeper=4.4.3 47 | - rails="~> 5.2.4.1" grape=1.1.0 doorkeeper=5.0.2 48 | - rails="~> 5.2.4.1" grape=1.1.0 doorkeeper=5.1.0 49 | - rails="~> 5.2.4.1" grape=1.1.0 doorkeeper=5.2.3 50 | - rails="~> 5.2.4.1" grape=1.2.5 doorkeeper=4.4.3 51 | - rails="~> 5.2.4.1" grape=1.2.5 doorkeeper=5.0.2 52 | - rails="~> 5.2.4.1" grape=1.2.5 doorkeeper=5.1.0 53 | - rails="~> 5.2.4.1" grape=1.2.5 doorkeeper=5.2.3 54 | - rails="~> 5.2.4.1" grape=1.3.0 doorkeeper=4.4.3 55 | - rails="~> 5.2.4.1" grape=1.3.0 doorkeeper=5.0.2 56 | - rails="~> 5.2.4.1" grape=1.3.0 doorkeeper=5.1.0 57 | - rails="~> 5.2.4.1" grape=1.3.0 doorkeeper=5.2.3 58 | - rails="~> 6.0.2" grape=1.0.3 doorkeeper=4.4.3 59 | - rails="~> 6.0.2" grape=1.0.3 doorkeeper=5.0.2 60 | - rails="~> 6.0.2" grape=1.0.3 doorkeeper=5.1.0 61 | - rails="~> 6.0.2" grape=1.0.3 doorkeeper=5.2.3 62 | - rails="~> 6.0.2" grape=1.1.0 doorkeeper=4.4.3 63 | - rails="~> 6.0.2" grape=1.1.0 doorkeeper=5.0.2 64 | - rails="~> 6.0.2" grape=1.1.0 doorkeeper=5.1.0 65 | - rails="~> 6.0.2" grape=1.1.0 doorkeeper=5.2.3 66 | - rails="~> 6.0.2" grape=1.2.5 doorkeeper=4.4.3 67 | - rails="~> 6.0.2" grape=1.2.5 doorkeeper=5.0.2 68 | - rails="~> 6.0.2" grape=1.2.5 doorkeeper=5.1.0 69 | - rails="~> 6.0.2" grape=1.2.5 doorkeeper=5.2.3 70 | - rails="~> 6.0.2" grape=1.3.0 doorkeeper=4.4.3 71 | - rails="~> 6.0.2" grape=1.3.0 doorkeeper=5.0.2 72 | - rails="~> 6.0.2" grape=1.3.0 doorkeeper=5.1.0 73 | - rails="~> 6.0.2" grape=1.3.0 doorkeeper=5.2.3 74 | 75 | jobs: 76 | exclude: 77 | - rvm: 2.4.5 78 | env: rails="~> 6.0.2" grape=1.0.3 doorkeeper=4.4.3 79 | - rvm: 2.4.5 80 | env: rails="~> 6.0.2" grape=1.0.3 doorkeeper=5.0.2 81 | - rvm: 2.4.5 82 | env: rails="~> 6.0.2" grape=1.0.3 doorkeeper=5.1.0 83 | - rvm: 2.4.5 84 | env: rails="~> 6.0.2" grape=1.0.3 doorkeeper=5.2.3 85 | - rvm: 2.4.5 86 | env: rails="~> 6.0.2" grape=1.1.0 doorkeeper=4.4.3 87 | - rvm: 2.4.5 88 | env: rails="~> 6.0.2" grape=1.1.0 doorkeeper=5.0.2 89 | - rvm: 2.4.5 90 | env: rails="~> 6.0.2" grape=1.1.0 doorkeeper=5.1.0 91 | - rvm: 2.4.5 92 | env: rails="~> 6.0.2" grape=1.1.0 doorkeeper=5.2.3 93 | - rvm: 2.4.5 94 | env: rails="~> 6.0.2" grape=1.2.5 doorkeeper=4.4.3 95 | - rvm: 2.4.5 96 | env: rails="~> 6.0.2" grape=1.2.5 doorkeeper=5.0.2 97 | - rvm: 2.4.5 98 | env: rails="~> 6.0.2" grape=1.2.5 doorkeeper=5.1.0 99 | - rvm: 2.4.5 100 | env: rails="~> 6.0.2" grape=1.2.5 doorkeeper=5.2.3 101 | - rvm: 2.4.5 102 | env: rails="~> 6.0.2" grape=1.3.0 doorkeeper=4.4.3 103 | - rvm: 2.4.5 104 | env: rails="~> 6.0.2" grape=1.3.0 doorkeeper=5.0.2 105 | - rvm: 2.4.5 106 | env: rails="~> 6.0.2" grape=1.3.0 doorkeeper=5.1.0 107 | - rvm: 2.4.5 108 | env: rails="~> 6.0.2" grape=1.3.0 doorkeeper=5.2.3 109 | 110 | 111 | script: 112 | #- bundle exec rubocop -DESP # Backend add in future. 113 | - bundle exec rake # Backend specs 114 | 115 | # Pipe the coverage data to Code Climate 116 | after_script: 117 | - ./cc-test-reporter format-coverage -t simplecov -o coverage/codeclimate.backend.json coverage/backend/.resultset.json # Format backend coverage 118 | - ./cc-test-reporter sum-coverage coverage/codeclimate.*.json -p 2 # Sum both coverage parts into coverage/codeclimate.json 119 | - if [[ "$TRAVIS_TEST_RESULT" == 0 ]]; then ./cc-test-reporter upload-coverage; fi # Upload coverage/codeclimate.json 120 | 121 | notifications: 122 | hipchat: 123 | rooms: 124 | secure: SUWenlDzlDbpryO1QzD+rN4MxIBpAAzwsFqnnkyRQ11thRVdvKuT2TUd+RlYImLXDNkvNjqmpXh7mihtcro9g8unR3nF1UKbuAPIv2kCklsio0jAnjVn7+h1l56hsa90Jy9t/YpKtoLx2QNWLz70n8VrtGJMAt53T6tZdgNUp58= 125 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | ## Unreleased 5 | 6 | ## 1.0.4 7 | 8 | * [#78](https://github.com/antek-drzewiecki/wine_bouncer/pull/78): Grape support for 1.2.x. Thank you @eitoball 9 | * [#77](https://github.com/antek-drzewiecki/wine_bouncer/pull/77): Upgrade to doorkeeper 5.0. Thank you @JWesorick 10 | 11 | ## 1.0.3 12 | * [#76](https://github.com/antek-drzewiecki/wine_bouncer/pull/76): Updated test matrix to test against recent versions. 13 | * [#74](https://github.com/antek-drzewiecki/wine_bouncer/pull/74): Support for Doorkeeper < 5.0. Thanks @stereoscott 14 | * [#72](https://github.com/antek-drzewiecki/wine_bouncer/pull/72): Support for Grape 1.1.x. Thanks @gustavolobo 15 | 16 | ## 1.0.2 17 | * [#68](https://github.com/antek-drzewiecki/wine_bouncer/pull/68): Update dependency to allow grape v1. Thanks @chandeeland 18 | 19 | ## 1.0.1 20 | * [#65](https://github.com/antek-drzewiecki/wine_bouncer/pull/65): Support for Doorkeeper 4.1 and 4.2. Thanks @daveallie 21 | 22 | ## 1.0 23 | * [#61](https://github.com/antek-drzewiecki/wine_bouncer/pull/61): Travis cleanup Rails 4.1.x is EOL. Ruby 2.0 is unsupported. 24 | * [#60](https://github.com/antek-drzewiecki/wine_bouncer/pull/60): Rails 5, Doorkeeper 4, grape 0.16.2 support. Thanks @texpert 25 | 26 | ## 0.5.1 27 | * [#57](https://github.com/antek-drzewiecki/wine_bouncer/pull/57): Removed locks for doorkeeper 28 | * [#56](https://github.com/antek-drzewiecki/wine_bouncer/pull/56): Grape 0.14.x support, removed locks. 29 | * Tested against ruby 2.3.0 30 | 31 | ## 0.5.0 32 | * [#50](https://github.com/antek-drzewiecki/wine_bouncer/pull/50): Grape 0.13.x support 33 | * [#48](https://github.com/antek-drzewiecki/wine_bouncer/pull/48): Bind Doorkeeper error response into WineBouncer errors 34 | * [#47](https://github.com/antek-drzewiecki/wine_bouncer/pull/47): Doorkeeper 3.0.x support 35 | 36 | ## 0.4.0 37 | * [#42](https://github.com/antek-drzewiecki/wine_bouncer/pull/42): Added support for Doorkeeper 2.2 38 | * [#41](https://github.com/antek-drzewiecki/wine_bouncer/pull/41): Added support for Grape 0.12.0, Removed support for Grape 0.8 and 0.9 (though they still work). 39 | * [#39](https://github.com/antek-drzewiecki/wine_bouncer/pull/39): Add option to disable WineBouncer conditionally. Thanks @Fryie . 40 | 41 | ## 0.3.1 42 | * [#31](https://github.com/antek-drzewiecki/wine_bouncer/pull/31): Improves support for default scopes trough DSL. 43 | * [#30](https://github.com/antek-drzewiecki/wine_bouncer/pull/30): Restricted grape dependencies to the next minor level of grape. 44 | * [#29](https://github.com/antek-drzewiecki/wine_bouncer/pull/29): Doorkeepers dependencies are restricted to minor levels. Thanks @nickcharlton 45 | * [#27](https://github.com/antek-drzewiecki/wine_bouncer/pull/27): Fixes DSL default and protected strategy. Fixes #24 and #26. 46 | 47 | ## 0.3.0 48 | * [#21](https://github.com/antek-drzewiecki/wine_bouncer/pull/21): Added an Easy DSL for WineBouncer. Thanks @masarakki . 49 | * [#23](https://github.com/antek-drzewiecki/wine_bouncer/pull/23): Added support for Doorkeeper 2.1.1 and refactored strategies. 50 | 51 | ## 0.2.2 52 | * [#17](https://github.com/antek-drzewiecki/wine_bouncer/pull/17): Added a new protected strategy. Thanks @whatasunnyday . 53 | 54 | ## 0.2.1 55 | * [#12](https://github.com/antek-drzewiecki/wine_bouncer/pull/12): Added a rails generator to generate the WineBouncer configuration file. Thanks @whatasunnyday. 56 | * [#7](https://github.com/antek-drzewiecki/wine_bouncer/pull/7): Added support for Doorkeeper 2.0.0 and 2.0.1. Thanks @whatasunnyday . 57 | 58 | ## 0.2.0 59 | * [#4](https://github.com/antek-drzewiecki/wine_bouncer/pull/4): Support for newer versions of grape ( > 0.8 ). 60 | * [#6](https://github.com/antek-drzewiecki/wine_bouncer/pull/6): Added the option to configure the resource owner. 61 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | ## Fork the project 5 | 6 | Fork the project on the [project page]( https://github.com/Antek-drzewiecki/wine_bouncer/fork ) 7 | 8 | Checkout the project from your own repository. 9 | 10 | ``` 11 | git clone https://github.com/[your github username]/wine_bouncer.git 12 | cd wine_bouncer 13 | git remote add upstream https://github.com/Antek-drzewiecki/wine_bouncer.git 14 | ``` 15 | 16 | ## Create your feature branch 17 | 18 | Make sure your fork is [up to date](https://help.github.com/articles/syncing-a-fork) and make a feature from the master branch. 19 | 20 | `git checkout -b my-new-feature` 21 | 22 | ## Prepare your development environment 23 | 24 | See [README](README.md#development) 25 | 26 | Run your specs to make sure nothing is broken ;) 27 | 28 | ## Write Tests and/or Code 29 | 30 | We appreciate pull requests. Even specs only to identify an problem! 31 | 32 | Because we want to ensure the quality of our gem. We cannot accept functional changes without tests. 33 | 34 | ## Write documentation 35 | 36 | Documentation is appreciated. Its nice to know people know how to use features. 37 | Also feel free to update the changelog with the changes. 38 | 39 | ## Commit your changes 40 | 41 | Commit your changes. 42 | `git add ...` 43 | `git commit -m 'Add some feature'` 44 | 45 | ## Push your changes 46 | 47 | Push to the branch. 48 | 49 | `git push origin my-new-feature` 50 | 51 | ## Create a pull request 52 | 53 | Check your feature branch, once its passes Travis-CI, create a pull request 54 | 55 | Thanks you for participating! 56 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | ENV['grape'] ||= '1.3.0' 6 | ENV['rails'] ||= '~> 6.0.0' 7 | ENV['doorkeeper'] ||= '5.0.0' 8 | 9 | gem 'rails', ENV['rails'] 10 | # ActiveRecord 5 needs sqlite3 ~> 1.3 and ActiveRectod 6 needs ~>1.4 11 | gem 'sqlite3', ENV['rails'].match(/5\.\d\.\d/) ? '~> 1.3.6' : '~> 1.4.2' 12 | 13 | # Change in the rack response API broke grape <1.3 14 | # Version lock rack for those versions of grape 15 | gem 'rack', '2.0.8' unless ENV['grape'].match(/1\.3\.\d/) 16 | 17 | gem 'doorkeeper', ENV['doorkeeper'] 18 | gem 'grape', ENV['grape'] 19 | 20 | # Specify your gem's dependencies in wine_bouncer.gemspec 21 | gemspec 22 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Antek Drzewiecki 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WineBouncer 2 | 3 | [![Build Status](https://travis-ci.org/antek-drzewiecki/wine_bouncer.svg?branch=master)](https://travis-ci.org/antek-drzewiecki/wine_bouncer) 4 | [![Code Climate](https://codeclimate.com/github/antek-drzewiecki/wine_bouncer/badges/gpa.svg)](https://codeclimate.com/github/antek-drzewiecki/wine_bouncer) 5 | [![Gem Version](https://badge.fury.io/rb/wine_bouncer.svg)](http://badge.fury.io/rb/wine_bouncer) 6 | [![Documentation](https://inch-ci.org/github/antek-drzewiecki/wine_bouncer.svg)](https://inch-ci.org/github/antek-drzewiecki/wine_bouncer) 7 | 8 | Protect your precious Grape API with Doorkeeper. 9 | WineBouncer uses minimal modification, to make the magic happen. 10 | 11 | Table of Contents 12 | ================= 13 | * [Requirements](#requirements) 14 | * [Installation](#installation) 15 | * [Upgrading](#upgrading) 16 | * [Usage](#usage) 17 | * [Easy DSL](#easy-dsl) 18 | * [Authentication strategies](#authentication-strategies) 19 | * [Default](#default) 20 | * [Swagger](#swagger) 21 | * [Protected](#protected) 22 | * [Token information](#token-information) 23 | * [Disable WineBouncer](#disable-winebouncer) 24 | * [Exceptions and Exception handling](#exceptions-and-exception-handling) 25 | * [Example Application](#example-application) 26 | * [Development](#development) 27 | * [Contributing](#contributing) 28 | 29 | 30 | ## Requirements 31 | - Ruby > 2.1 32 | - Doorkeeper > 1.4.0 and < 5 33 | - Grape > 0.10 and < 1.2 34 | 35 | Please submit pull requests and Travis env bumps for newer dependency versions. 36 | 37 | ## Installation 38 | 39 | Add this line to your application's Gemfile: 40 | 41 | ```ruby 42 | gem 'wine_bouncer', '~> 1.0.4' 43 | ``` 44 | 45 | And then execute: 46 | 47 | ```ruby 48 | bundle 49 | ``` 50 | 51 | ## Upgrading 52 | When upgrading from a previous version, see [UPGRADING](UPGRADING.md). You might also be interested at the [CHANGELOG](CHANGELOG.md). 53 | 54 | 55 | ## Usage 56 | WineBouncer is a custom Grape Middleware used for Authentication and Authorization. We assume you have a Grape API mounted in your Rails application together with Doorkeeper. 57 | 58 | To get started with WineBouncer, run the configuration initializer: 59 | 60 | ```shell 61 | $ rails g wine_bouncer:initializer 62 | ``` 63 | 64 | 65 | This creates a rails initializer in your Rails app at `config/initializers/wine_bouncer.rb` with the following configuration: 66 | 67 | ``` ruby 68 | WineBouncer.configure do |config| 69 | config.auth_strategy = :default 70 | 71 | config.define_resource_owner do 72 | User.find(doorkeeper_access_token.resource_owner_id) if doorkeeper_access_token 73 | end 74 | end 75 | ``` 76 | 77 | Then register WineBouncer as Grape middleware in your Grape API. 78 | 79 | ``` ruby 80 | class Api < Grape::API 81 | default_format :json 82 | format :json 83 | use ::WineBouncer::OAuth2 84 | mount YourAwesomeApi 85 | end 86 | ``` 87 | 88 | ### Easy DSL 89 | 90 | WineBouncer comes with an easy DSL and relies on Grape's DSL extentions to define if an endpoint method should be protected. 91 | You can protect an endpoint by calling `oauth2` method with optional scopes in front of the endpoint definition. 92 | 93 | ``` ruby 94 | class MyAwesomeAPI < Grape::API 95 | desc 'protected method with required public scope' 96 | oauth2 'public' 97 | get '/protected' do 98 | { hello: 'world' } 99 | end 100 | 101 | oauth2 'public' 102 | get '/with_no_description' do 103 | { hello: 'undescribed world' } 104 | end 105 | 106 | desc 'Unprotected method' 107 | get '/unprotected' do 108 | { hello: 'unprotected world' } 109 | end 110 | 111 | desc 'This method needs the public or private scope.' 112 | oauth2 'public', 'write' 113 | get '/method' do 114 | { hello: 'public or private user.' } 115 | end 116 | 117 | desc 'This method uses Doorkeepers default scopes.', 118 | oauth2 119 | get '/protected_with_default_scope' do 120 | { hello: 'protected unscoped world' } 121 | end 122 | end 123 | 124 | class Api < Grape::API 125 | default_format :json 126 | format :json 127 | use ::WineBouncer::OAuth2 128 | mount MyAwesomeAPI 129 | end 130 | ``` 131 | 132 | ### Authentication strategies 133 | 134 | Behaviour of the authentication can be customized by selecting an authentication strategy. The following authentication strategies are provided in the gem. 135 | 136 | #### Default 137 | The default strategy only authenticates endpoints which are annotated by the `oauth2` method. Un-annotated endpoints still can be accessed without authentication. 138 | 139 | #### Swagger 140 | 141 | WineBouncer comes with a strategy that can be perfectly used with [grape-swagger](https://github.com/tim-vandecasteele/grape-swagger) with a syntax compliant with the [swagger spec](https://github.com/wordnik/swagger-spec/). 142 | This might be one of the simplest methods to protect your API and serve it with documentation. You can use [swagger-ui](https://github.com/wordnik/swagger-ui) to view your documentation. 143 | 144 | To get started ensure you also have included the `grape-swagger` gem in your gemfile. 145 | 146 | Run `bundle` to install the missing gems. 147 | 148 | Create a rails initializer in your Rails app at `config/initializers/wine_bouncer.rb` with the following configuration. 149 | 150 | ``` ruby 151 | WineBouncer.configure do |config| 152 | config.auth_strategy = :swagger 153 | 154 | config.define_resource_owner do 155 | User.find(doorkeeper_access_token.resource_owner_id) if doorkeeper_access_token 156 | end 157 | end 158 | ``` 159 | 160 | Then you can start protecting your API like the example below. 161 | 162 | ``` ruby 163 | desc 'This method needs the public or private scope.', 164 | success: Api::Entities::Response, 165 | failure: [ 166 | [401, 'Unauthorized', Api::Entities::Error], 167 | [403, 'Forbidden', Api::Entities::Error] 168 | ], 169 | notes: <<-NOTE 170 | Marked down notes! 171 | NOTE 172 | oauth2 'public', 'write' 173 | get '/method' do 174 | { hello: 'public or private user.' } 175 | end 176 | ``` 177 | 178 | The Swagger strategy uses scopes and injects them in the authorizations description for external application to be read. 179 | 180 | #### Protected 181 | 182 | The protected strategy is very similar to the default strategy except any public endpoint must explicitly set. To make an end point public, use `oauth2 false`. 183 | If the authorization method is not set, the end point is assumed to be __protected with Doorkeeper's default scopes__ (which is the same as `oauth2 nil `.) 184 | To protect your endpoint with other scopes append the following method `oauth2 'first scope', 'second scope'`. 185 | 186 | 187 | ### Token information 188 | 189 | WineBouncer comes with free extras! Methods for `resource_owner` and `doorkeeper_access_token` get included in your endpoints. You can use them to get the current resource owner, and the access_token object of doorkeeper. 190 | 191 | ### Disable WineBouncer 192 | 193 | If you want to disable WineBouncer conditionally - e.g. in specs - you can add a block to the WineBouncer configuration. When this block evaluates to true, any request will be unprotected. For example: 194 | ```{ruby} 195 | WineBouncer.configure do |config| 196 | config.disable do 197 | Rails.env.test? 198 | end 199 | end 200 | ``` 201 | 202 | The block is newly evaluated for every request, so you could in principle have something like: 203 | ```{ruby} 204 | config.disable do 205 | [true, false].sample 206 | end 207 | ``` 208 | 209 | You probably shouldn't, though. 210 | 211 | ## Exceptions and Exception handling 212 | 213 | This gem raises the following exceptions which can be handled in your Grape API, see [Grape documentation](https://github.com/intridea/grape#exception-handling). 214 | 215 | * `WineBouncer::Errors::OAuthUnauthorizedError` 216 | when the request is unauthorized. 217 | * `WineBouncer::Errors::OAuthForbiddenError` 218 | when the token is found but scopes do not match. 219 | 220 | Detailed doorkeeper error response can be found in the error's `response` attribute. You could use 221 | it to compose the actual HTTP response to API users. 222 | 223 | ## Example/Template Application 224 | 225 | A full working sample app (or starter template) can be found at [grape-doorkeeper on github](https://github.com/sethherr/grape-doorkeeper). It has one click deploy to Heroku and [a live example](https://grape-doorkeeper.herokuapp.com/). 226 | 227 | ## Development 228 | 229 | Since we want the gem tested against several rails versions we use the same way to prepare our development environment as Doorkeeper. 230 | 231 | To install the development environment for rails 4.2.6, you can also specify a different rails version to test against. 232 | 233 | `rails=4.2.6 bundle install` 234 | 235 | To run the specs. 236 | 237 | `rails=4.2.6 bundle exec rake` 238 | 239 | ## Contributing 240 | 241 | For contributing to the gem see [CONTRIBUTING](CONTRIBUTING.md). 242 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'bundler/gem_tasks' 4 | 5 | require 'rspec/core/rake_task' 6 | 7 | desc 'Default: Run all specs.' 8 | task default: :spec 9 | 10 | desc 'Run all specs' 11 | RSpec::Core::RakeTask.new(:spec) 12 | -------------------------------------------------------------------------------- /UPGRADING.md: -------------------------------------------------------------------------------- 1 | Upgrading WineBouncer 2 | ===================== 3 | 4 | 5 | ## Upgrading to >= 1.0 6 | 7 | No DSL changes have been made. This is just a stability release from we can work to next releases. 8 | 9 | 10 | ## Upgrading to >= 0.5.0 11 | 12 | WineBouncer's exceptions `OAuthUnauthorizedError` and `OAuthForbiddenError` now come with a 13 | corresponding Doorkeeper's error response. You can access it via `response` method of the exception. 14 | For backward compatible, you can still use `message` but the content will be provided by Doorkeeper 15 | error response's description. 16 | 17 | ## Upgrading to >= 0.3.0 18 | 19 | An new DSL has been introduced to WineBouncer. This DSL will become the preferred way to authorize your endpoints. 20 | The authentication trough endpoint description will become deprecated in the future version. 21 | 22 | The old way to authorize your endpoints: 23 | 24 | ``` 25 | desc 'protected method with required public and private scope', 26 | auth: { scopes: ['public','private'] } 27 | get '/protected' do 28 | { hello: 'world' } 29 | end 30 | ``` 31 | 32 | You may now write: 33 | ``` 34 | desc 'protected method with required public and private scope' 35 | oauth2 'public', 'private' 36 | get '/protected' do 37 | { hello: 'world' } 38 | end 39 | ``` 40 | 41 | And even remove the description. 42 | 43 | Note this is the last version that will support Grape 0.8 and 0.9. Grape 0.10 will be the next minimum Grape version. 44 | 45 | ### Upgrading to >= 0.2.0 46 | 47 | To use the `resource_owner` you now need to define how WineBouncer gets the resource owner. In most cases he needs to return the User that is logged in, but not in all cases. 48 | You now additionally need to specify `define_resource_owner` in your configuration. Mostly this the following code will work: 49 | 50 | ``` ruby 51 | WineBouncer.configure do |c| 52 | ....... 53 | c.define_resource_owner do 54 | User.find(doorkeeper_access_token.resource_owner_id) if doorkeeper_access_token 55 | end 56 | end 57 | ``` 58 | 59 | The required Grape version is now from 0.8 and above. 60 | 61 | ------ 62 | From version 0.1.0 upgrade instructions are given. 63 | -------------------------------------------------------------------------------- /lib/generators/templates/wine_bouncer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | WineBouncer.configure do |config| 4 | config.auth_strategy = :default 5 | 6 | config.define_resource_owner do 7 | User.find(doorkeeper_access_token.resource_owner_id) if doorkeeper_access_token 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/generators/wine_bouncer/initializer_generator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module WineBouncer 4 | module Generators 5 | class InitializerGenerator < ::Rails::Generators::Base 6 | source_root File.expand_path('../../templates', __FILE__) 7 | 8 | desc 'Creates a sample WineBouncer initializer.' 9 | def copy_initializer 10 | copy_file 'wine_bouncer.rb', 'config/initializers/wine_bouncer.rb' 11 | end 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/wine_bouncer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'grape' 4 | require 'doorkeeper' 5 | require 'wine_bouncer/version' 6 | require 'wine_bouncer/errors' 7 | require 'wine_bouncer/configuration' 8 | require 'wine_bouncer/oauth2' 9 | require 'wine_bouncer/base_strategy' 10 | require 'wine_bouncer/auth_methods/auth_methods' 11 | require 'wine_bouncer/extension' 12 | 13 | module WineBouncer 14 | end 15 | -------------------------------------------------------------------------------- /lib/wine_bouncer/auth_methods/auth_methods.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module WineBouncer 4 | module AuthMethods 5 | 6 | def protected_endpoint=(protected) 7 | @protected_endpoint = protected 8 | end 9 | 10 | def protected_endpoint? 11 | @protected_endpoint || false 12 | end 13 | 14 | def resource_owner 15 | instance_eval(&WineBouncer.configuration.defined_resource_owner) 16 | end 17 | 18 | def client_credential_token? 19 | has_doorkeeper_token? && doorkeeper_access_token.resource_owner_id.nil? 20 | end 21 | 22 | def doorkeeper_access_token 23 | @_doorkeeper_access_token 24 | end 25 | 26 | def doorkeeper_access_token=(token) 27 | @_doorkeeper_access_token = token 28 | end 29 | 30 | def has_doorkeeper_token? 31 | !@_doorkeeper_access_token.nil? 32 | end 33 | 34 | def has_resource_owner? 35 | has_doorkeeper_token? && !!doorkeeper_access_token.resource_owner_id 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/wine_bouncer/auth_strategies/default.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module WineBouncer 4 | module AuthStrategies 5 | class Default < ::WineBouncer::BaseStrategy 6 | def endpoint_protected? 7 | !!endpoint_authorizations 8 | end 9 | 10 | def has_auth_scopes? 11 | !!endpoint_authorizations && 12 | endpoint_authorizations.key?(:scopes) && 13 | !endpoint_authorizations[:scopes].empty? 14 | end 15 | 16 | def auth_scopes 17 | endpoint_authorizations[:scopes].map(&:to_sym) 18 | end 19 | 20 | private 21 | 22 | def endpoint_authorizations 23 | api_context.options[:route_options][:auth] 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/wine_bouncer/auth_strategies/protected.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module WineBouncer 4 | module AuthStrategies 5 | class Protected < WineBouncer::BaseStrategy 6 | def endpoint_protected? 7 | has_authorizations? 8 | end 9 | 10 | def has_auth_scopes? 11 | endpoint_authorizations && 12 | endpoint_authorizations.key?(:scopes) && 13 | endpoint_authorizations[:scopes].any? 14 | end 15 | 16 | def auth_scopes 17 | endpoint_authorizations[:scopes].map(&:to_sym) 18 | end 19 | 20 | private 21 | 22 | def nil_authorizations? 23 | endpoint_authorizations.nil? 24 | end 25 | 26 | # returns true if an authorization hash has been found 27 | # First it checks for the old syntax, then for the new. 28 | def has_authorizations? 29 | (nil_authorizations? || !!endpoint_authorizations) && scope_keys? 30 | end 31 | 32 | # if false or nil scopes are entered the authorization should be skipped. 33 | # nil_authorizations? is used to check against the legacy hash. 34 | def scope_keys? 35 | nil_authorizations? || endpoint_authorizations[:scopes] != [false] 36 | end 37 | 38 | def endpoint_authorizations 39 | api_context.options[:route_options][:auth] 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/wine_bouncer/auth_strategies/swagger.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module WineBouncer 4 | module AuthStrategies 5 | class Swagger < ::WineBouncer::BaseStrategy 6 | def endpoint_protected? 7 | has_authorizations? && !!authorization_type_oauth2 8 | end 9 | 10 | def has_auth_scopes? 11 | endpoint_protected? && !authorization_type_oauth2.empty? 12 | end 13 | 14 | def auth_scopes 15 | authorization_type_oauth2.map { |hash| hash[:scope].to_sym } 16 | end 17 | 18 | private 19 | 20 | def has_authorizations? 21 | !!endpoint_authorizations 22 | end 23 | 24 | def endpoint_authorizations 25 | api_context.options[:route_options][:authorizations] 26 | end 27 | 28 | def authorization_type_oauth2 29 | endpoint_authorizations[:oauth2] 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/wine_bouncer/base_strategy.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module WineBouncer 4 | class BaseStrategy 5 | attr_accessor :api_context 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/wine_bouncer/configuration.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module WineBouncer 4 | 5 | class << self 6 | attr_accessor :configuration 7 | end 8 | 9 | class Configuration 10 | attr_accessor :auth_strategy 11 | attr_accessor :defined_resource_owner 12 | attr_writer :auth_strategy 13 | 14 | def auth_strategy 15 | @auth_strategy || :default 16 | end 17 | 18 | def require_strategies 19 | require "wine_bouncer/auth_strategies/#{auth_strategy}" 20 | end 21 | 22 | def define_resource_owner &block 23 | raise(ArgumentError, 'define_resource_owner expects a block in the configuration') unless block_given? 24 | @defined_resource_owner = block 25 | end 26 | 27 | def defined_resource_owner 28 | raise(Errors::UnconfiguredError, 'Please define define_resource_owner to configure the resource owner') unless @defined_resource_owner 29 | @defined_resource_owner 30 | end 31 | 32 | # when the block evaluates to true, WineBouncer should be disabled 33 | # if no block is provided, WineBouncer is always enabled 34 | def disable(&block) 35 | @disable_block = block 36 | end 37 | 38 | def disable_block 39 | @disable_block || ->() { false } 40 | end 41 | end 42 | 43 | def self.configuration 44 | @config || raise(Errors::UnconfiguredError.new) 45 | end 46 | 47 | def self.configuration=(config) 48 | @config = config 49 | @config.require_strategies 50 | end 51 | 52 | ### 53 | # Configure block. 54 | # Requires all strategy specific files. 55 | ### 56 | def self.configure 57 | yield(config) 58 | config.require_strategies 59 | config 60 | end 61 | 62 | private_class_method 63 | 64 | ### 65 | # Returns a new configuration or existing one. 66 | ### 67 | def self.config 68 | @config ||= Configuration.new 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /lib/wine_bouncer/errors.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module WineBouncer 4 | module Errors 5 | class UnconfiguredError < StandardError; end 6 | 7 | class OAuthUnauthorizedError < StandardError 8 | attr_reader :response 9 | def initialize(response) 10 | super(response.try(:description)) 11 | @response = response 12 | end 13 | end 14 | 15 | class OAuthForbiddenError < StandardError 16 | attr_reader :response 17 | def initialize(response) 18 | super(response.try(:description)) 19 | @response = response 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/wine_bouncer/extension.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module WineBouncer 4 | module Extension 5 | def oauth2(*scopes) 6 | scopes = Doorkeeper.configuration.default_scopes.all if scopes.all? { |x| x.nil? } 7 | description = if respond_to?(:route_setting) # >= grape-0.10.0 8 | route_setting(:description) || route_setting(:description, {}) 9 | else 10 | @last_description ||= {} 11 | end 12 | # case WineBouncer.configuration.auth_strategy 13 | # when :default 14 | description[:auth] = { scopes: scopes } 15 | # when :swagger 16 | description[:authorizations] = { oauth2: scopes.map { |x| { scope: x } } } 17 | # end 18 | end 19 | 20 | # Grape::API::Instance is defined in grape 1.2.0 or above 21 | grape_api = defined?(Grape::API::Instance) ? Grape::API::Instance : Grape::API 22 | grape_api.extend self 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/wine_bouncer/oauth2.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module WineBouncer 4 | class OAuth2 < Grape::Middleware::Base 5 | include Doorkeeper::Helpers::Controller 6 | ### 7 | # returns the api context 8 | ### 9 | def context 10 | env['api.endpoint'] 11 | end 12 | 13 | ############ 14 | # DoorKeeper stuff. 15 | ############ 16 | 17 | ### 18 | # Sets and converts a rack request to a ActionDispatch request, which is required for DoorKeeper to function. 19 | ### 20 | def doorkeeper_request=(env) 21 | @_doorkeeper_request = ActionDispatch::Request.new(env) 22 | end 23 | 24 | ### 25 | # Returns the request context. 26 | ### 27 | def request 28 | @_doorkeeper_request 29 | end 30 | 31 | ### 32 | # Returns true if the doorkeeper token is valid, false otherwise. 33 | ### 34 | def valid_doorkeeper_token?(*scopes) 35 | doorkeeper_token && doorkeeper_token.acceptable?(scopes) 36 | end 37 | 38 | ############ 39 | # Authorization control. 40 | ############ 41 | 42 | ### 43 | # returns true if the endpoint is protected, otherwise false 44 | ### 45 | def endpoint_protected? 46 | auth_strategy.endpoint_protected? 47 | end 48 | 49 | ### 50 | # Returns all auth scopes from an protected endpoint. 51 | # [ nil ] if none, otherwise an array of [ :scopes ] 52 | ### 53 | def auth_scopes 54 | return *nil unless auth_strategy.has_auth_scopes? 55 | auth_strategy.auth_scopes 56 | end 57 | 58 | ### 59 | # This method handles the authorization, raises errors if authorization has failed. 60 | ### 61 | def doorkeeper_authorize!(*scopes) 62 | scopes = Doorkeeper.configuration.default_scopes if scopes.empty? 63 | unless valid_doorkeeper_token?(*scopes) 64 | if !doorkeeper_token || !doorkeeper_token.accessible? 65 | error = Doorkeeper::OAuth::InvalidTokenResponse.from_access_token(doorkeeper_token) 66 | raise WineBouncer::Errors::OAuthUnauthorizedError, error 67 | else 68 | error = Doorkeeper::OAuth::ForbiddenTokenResponse.from_scopes(scopes) 69 | raise WineBouncer::Errors::OAuthForbiddenError, error 70 | end 71 | end 72 | end 73 | 74 | ############ 75 | # Grape middleware methods 76 | ############ 77 | 78 | ### 79 | # Before do. 80 | ### 81 | def before 82 | return if WineBouncer.configuration.disable_block.call 83 | 84 | set_auth_strategy(WineBouncer.configuration.auth_strategy) 85 | auth_strategy.api_context = context 86 | #extend the context with auth methods. 87 | context.extend(WineBouncer::AuthMethods) 88 | context.protected_endpoint = endpoint_protected? 89 | return unless context.protected_endpoint? 90 | self.doorkeeper_request = env # set request for later use. 91 | doorkeeper_authorize!(*auth_scopes) 92 | context.doorkeeper_access_token = doorkeeper_token 93 | end 94 | 95 | ### 96 | # Strategy 97 | ### 98 | attr_reader :auth_strategy 99 | 100 | private 101 | 102 | def set_auth_strategy(strategy) 103 | @auth_strategy = WineBouncer::AuthStrategies.const_get(strategy.to_s.capitalize.to_s).new 104 | end 105 | end 106 | end 107 | -------------------------------------------------------------------------------- /lib/wine_bouncer/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module WineBouncer 4 | VERSION = '1.0.4'.freeze 5 | end 6 | -------------------------------------------------------------------------------- /spec/dummy/README.rdoc: -------------------------------------------------------------------------------- 1 | == README 2 | 3 | This README would normally document whatever steps are necessary to get the 4 | application up and running. 5 | 6 | Things you may want to cover: 7 | 8 | * Ruby version 9 | 10 | * System dependencies 11 | 12 | * Configuration 13 | 14 | * Database creation 15 | 16 | * Database initialization 17 | 18 | * How to run the test suite 19 | 20 | * Services (job queues, cache servers, search engines, etc.) 21 | 22 | * Deployment instructions 23 | 24 | * ... 25 | 26 | 27 | Please feel free to use a different markup language if you do not plan to run 28 | rake doc:app. 29 | -------------------------------------------------------------------------------- /spec/dummy/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /spec/dummy/app/api/default_api.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Api 4 | 5 | ### 6 | # Api under test, default doorkeeper scope is 'account' 7 | ## 8 | class MountedDefaultApiUnderTest < Grape::API 9 | desc 'Protected method with public', auth: { scopes: ['public'] } 10 | get '/protected' do 11 | { hello: 'world' } 12 | end 13 | 14 | desc 'Protected method with private', auth: { scopes: ['private'] } 15 | get '/protected_with_private_scope' do 16 | { hello: 'scoped world' } 17 | end 18 | 19 | desc 'Protected method with custom scope', auth: { scopes: ['custom_scope'] } 20 | get '/oauth2_custom_scope' do 21 | { hello: 'oauth2_custom_scope' } 22 | end 23 | 24 | desc 'Unprotected method' 25 | get '/unprotected' do 26 | { hello: 'unprotected world' } 27 | end 28 | 29 | desc 'Protected method with public that returns the user name', auth: { scopes: ['public'] } 30 | get '/protected_user' do 31 | { hello: resource_owner.name } 32 | end 33 | 34 | desc 'This method uses Doorkeepers default scopes', auth: {} 35 | get '/protected_without_scope' do 36 | { hello: 'protected unscoped world' } 37 | end 38 | 39 | get '/not_described_world' do 40 | { hello: 'non described world' } 41 | end 42 | 43 | desc 'oauth2_dsl' 44 | oauth2 'public' 45 | get '/oauth2_dsl' do 46 | { hello: 'oauth2 dsl' } 47 | end 48 | 49 | oauth2 'custom_scope' 50 | get '/oauth2_dsl_custom_scope' do 51 | { hello: 'oauth2 dsl custom scope' } 52 | end 53 | 54 | oauth2 55 | get '/oauth2_dsl_default_scopes' do 56 | { hello: 'oauth dsl default scopes' } 57 | end 58 | 59 | oauth2 'multiple', 'scopes' 60 | get '/oauth2_dsl_multiple_scopes' do 61 | { hello: 'oauth2 dsl multiple scopes' } 62 | end 63 | end 64 | 65 | class DefaultApiUnderTest < Grape::API 66 | default_format :json 67 | format :json 68 | use ::WineBouncer::OAuth2 69 | mount MountedDefaultApiUnderTest 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /spec/dummy/app/api/protected_api.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Api 4 | 5 | ### 6 | # Api under test, default doorkeeper scope is 'account' 7 | ## 8 | class MountedProtectedApiUnderTest < Grape::API 9 | desc 'Protected method with public', auth: { scopes: ['public'] } 10 | get '/protected' do 11 | { hello: 'world' } 12 | end 13 | 14 | desc 'Protected method with private', auth: { scopes: ['private'] } 15 | get '/protected_with_private_scope' do 16 | { hello: 'scoped world' } 17 | end 18 | 19 | desc 'Unprotected method' 20 | get '/unprotected', auth: false do 21 | { hello: 'unprotected world' } 22 | end 23 | 24 | desc 'Protected method with public that returns the user name', auth: { scopes: ['public'] } 25 | get '/protected_user' do 26 | { hello: resource_owner.name } 27 | end 28 | 29 | desc 'This method uses Doorkeepers default scopes' 30 | get '/protected_without_scope' do 31 | { hello: 'protected unscoped world' } 32 | end 33 | 34 | desc 'oauth2_dsl' 35 | oauth2 'public' 36 | get '/oauth2_dsl' do 37 | { hello: 'oauth2_dsl' } 38 | end 39 | 40 | oauth2 'custom_scope' 41 | get '/oauth2_dsl_custom_scope' do 42 | { hello: 'custom scope' } 43 | end 44 | 45 | oauth2 46 | get '/oauth2_protected_with_default_scopes' do 47 | { hello: 'default oauth2 dsl' } 48 | end 49 | 50 | oauth2 false 51 | get '/unprotected_endpoint' do 52 | { hello: 'public oauth2 dsl' } 53 | end 54 | 55 | get '/not_described_world' do 56 | { hello: 'non described world' } 57 | end 58 | end 59 | 60 | class ProtectedApiUnderTest < Grape::API 61 | default_format :json 62 | format :json 63 | use ::WineBouncer::OAuth2 64 | mount MountedProtectedApiUnderTest 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /spec/dummy/app/api/swagger_api.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Api 4 | 5 | ### 6 | # Api under test, default doorkeeper scope is 'account' 7 | ## 8 | class MountedSwaggerApiUnderTest < Grape::API 9 | desc 'Protected method with public', authorizations: { oauth2: [{ scope: 'public', description: 'anything' }] } 10 | get '/protected' do 11 | { hello: 'world' } 12 | end 13 | 14 | desc 'Protected method with private', authorizations: { oauth2: [{ scope: 'private', description: 'anything' }] } 15 | get '/protected_with_private_scope' do 16 | { hello: 'scoped world' } 17 | end 18 | 19 | desc 'Unprotected method' 20 | get '/unprotected' do 21 | { hello: 'unprotected world' } 22 | end 23 | 24 | desc 'Protected method with public that returns the user name', authorizations: { oauth2: [{ scope: 'public', description: 'anything' }] } 25 | get '/protected_user' do 26 | { hello: resource_owner.name } 27 | end 28 | 29 | desc 'This method uses Doorkeepers default scopes', authorizations: { oauth2: [] } 30 | get '/protected_without_scope' do 31 | { hello: 'protected unscoped world' } 32 | end 33 | 34 | get '/not_described_world' do 35 | { hello: 'non described world' } 36 | end 37 | 38 | desc 'oauth2 dsl' 39 | oauth2 'public' 40 | get '/oauth2_dsl' do 41 | { hello: 'oauth2_dsl' } 42 | end 43 | 44 | oauth2 45 | get '/oauth2_dsl_default_scopes' do 46 | { hello: 'oauth dsl default scopes' } 47 | end 48 | 49 | oauth2 'custom_scope' 50 | get '/oauth2_dsl_custom_scopes' do 51 | { hello: 'oauth dsl custom scopes' } 52 | end 53 | end 54 | 55 | class SwaggerApiUnderTest < Grape::API 56 | default_format :json 57 | format :json 58 | use ::WineBouncer::OAuth2 59 | mount MountedSwaggerApiUnderTest 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /spec/dummy/app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /spec/dummy/app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antek-drzewiecki/wine_bouncer/c82b88f73c7adc43a8e89ca73f7e18952ec30de4/spec/dummy/app/assets/images/.keep -------------------------------------------------------------------------------- /spec/dummy/app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. 9 | // 10 | // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require_tree . 14 | -------------------------------------------------------------------------------- /spec/dummy/app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any styles 10 | * defined in the other CSS/SCSS files in this directory. It is generally better to create a new 11 | * file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | -------------------------------------------------------------------------------- /spec/dummy/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ApplicationController < ActionController::Base 4 | # Prevent CSRF attacks by raising an exception. 5 | # For APIs, you may want to use :null_session instead. 6 | protect_from_forgery with: :exception 7 | end 8 | -------------------------------------------------------------------------------- /spec/dummy/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antek-drzewiecki/wine_bouncer/c82b88f73c7adc43a8e89ca73f7e18952ec30de4/spec/dummy/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /spec/dummy/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ApplicationHelper 4 | end 5 | -------------------------------------------------------------------------------- /spec/dummy/app/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antek-drzewiecki/wine_bouncer/c82b88f73c7adc43a8e89ca73f7e18952ec30de4/spec/dummy/app/mailers/.keep -------------------------------------------------------------------------------- /spec/dummy/app/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antek-drzewiecki/wine_bouncer/c82b88f73c7adc43a8e89ca73f7e18952ec30de4/spec/dummy/app/models/.keep -------------------------------------------------------------------------------- /spec/dummy/app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antek-drzewiecki/wine_bouncer/c82b88f73c7adc43a8e89ca73f7e18952ec30de4/spec/dummy/app/models/concerns/.keep -------------------------------------------------------------------------------- /spec/dummy/app/models/user.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class User < ActiveRecord::Base 4 | end 5 | -------------------------------------------------------------------------------- /spec/dummy/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dummy 5 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> 6 | <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> 7 | <%= csrf_meta_tags %> 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /spec/dummy/bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /spec/dummy/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../../config/application', __FILE__) 3 | require_relative '../config/boot' 4 | require 'rails/commands' 5 | -------------------------------------------------------------------------------- /spec/dummy/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /spec/dummy/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run Rails.application 5 | -------------------------------------------------------------------------------- /spec/dummy/config/application.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require File.expand_path('boot', __dir__) 4 | 5 | # Pick the frameworks you want: 6 | require 'active_record/railtie' 7 | require 'action_controller/railtie' 8 | require 'action_mailer/railtie' 9 | require 'action_view/railtie' 10 | require 'sprockets/railtie' 11 | 12 | require 'wine_bouncer' 13 | # require "rails/test_unit/railtie" 14 | 15 | Bundler.require(*Rails.groups) 16 | 17 | module Dummy 18 | class Application < Rails::Application 19 | # Settings in config/environments/* take precedence over those specified here. 20 | # Application configuration should go into files in config/initializers 21 | # -- all .rb files in that directory are automatically loaded. 22 | 23 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 24 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 25 | # config.time_zone = 'Central Time (US & Canada)' 26 | 27 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 28 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 29 | # config.i18n.default_locale = :de 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/dummy/config/boot.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Set up gems listed in the Gemfile. 4 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../../Gemfile', __FILE__) 5 | 6 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) 7 | $LOAD_PATH.unshift File.expand_path('../../../../lib', __FILE__) 8 | -------------------------------------------------------------------------------- /spec/dummy/config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | # 7 | default: &default 8 | adapter: sqlite3 9 | pool: 5 10 | timeout: 5000 11 | 12 | development: 13 | <<: *default 14 | database: db/development.sqlite3 15 | 16 | # Warning: The database defined as "test" will be erased and 17 | # re-generated from your development database when you run "rake". 18 | # Do not set this db to the same as development or production. 19 | test: 20 | <<: *default 21 | database: ":memory:" 22 | 23 | production: 24 | <<: *default 25 | database: db/production.sqlite3 26 | -------------------------------------------------------------------------------- /spec/dummy/config/environment.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Load the Rails application. 4 | require File.expand_path('../application', __FILE__) 5 | 6 | # Initialize the Rails application. 7 | Rails.application.initialize! 8 | -------------------------------------------------------------------------------- /spec/dummy/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Rails.application.configure do 4 | # Settings specified here will take precedence over those in config/application.rb. 5 | 6 | # In the development environment your application's code is reloaded on 7 | # every request. This slows down response time but is perfect for development 8 | # since you don't have to restart the web server when you make code changes. 9 | config.cache_classes = false 10 | 11 | # Do not eager load code on boot. 12 | config.eager_load = false 13 | 14 | # Show full error reports and disable caching. 15 | config.consider_all_requests_local = true 16 | config.action_controller.perform_caching = false 17 | 18 | # Don't care if the mailer can't send. 19 | config.action_mailer.raise_delivery_errors = false 20 | 21 | # Print deprecation notices to the Rails logger. 22 | config.active_support.deprecation = :log 23 | 24 | # Raise an error on page load if there are pending migrations. 25 | config.active_record.migration_error = :page_load 26 | 27 | # Debug mode disables concatenation and preprocessing of assets. 28 | # This option may cause significant delays in view rendering with a large 29 | # number of complex assets. 30 | config.assets.debug = true 31 | 32 | # Adds additional error checking when serving assets at runtime. 33 | # Checks for improperly declared sprockets dependencies. 34 | # Raises helpful error messages. 35 | config.assets.raise_runtime_errors = true 36 | 37 | # Raises error for missing translations 38 | # config.action_view.raise_on_missing_translations = true 39 | end 40 | -------------------------------------------------------------------------------- /spec/dummy/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Rails.application.configure do 4 | # Settings specified here will take precedence over those in config/application.rb. 5 | 6 | # Code is not reloaded between requests. 7 | config.cache_classes = true 8 | 9 | # Eager load code on boot. This eager loads most of Rails and 10 | # your application in memory, allowing both threaded web servers 11 | # and those relying on copy on write to perform better. 12 | # Rake tasks automatically ignore this option for performance. 13 | config.eager_load = true 14 | 15 | # Full error reports are disabled and caching is turned on. 16 | config.consider_all_requests_local = false 17 | config.action_controller.perform_caching = true 18 | 19 | # Enable Rack::Cache to put a simple HTTP cache in front of your application 20 | # Add `rack-cache` to your Gemfile before enabling this. 21 | # For large-scale production use, consider using a caching reverse proxy like nginx, varnish or squid. 22 | # config.action_dispatch.rack_cache = true 23 | 24 | # Disable Rails's static asset server (Apache or nginx will already do this). 25 | config.serve_static_assets = false 26 | 27 | # Compress JavaScripts and CSS. 28 | config.assets.js_compressor = :uglifier 29 | # config.assets.css_compressor = :sass 30 | 31 | # Do not fallback to assets pipeline if a precompiled asset is missed. 32 | config.assets.compile = false 33 | 34 | # Generate digests for assets URLs. 35 | config.assets.digest = true 36 | 37 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 38 | 39 | # Specifies the header that your server uses for sending files. 40 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache 41 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx 42 | 43 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 44 | # config.force_ssl = true 45 | 46 | # Set to :debug to see everything in the log. 47 | config.log_level = :info 48 | 49 | # Prepend all log lines with the following tags. 50 | # config.log_tags = [ :subdomain, :uuid ] 51 | 52 | # Use a different logger for distributed setups. 53 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) 54 | 55 | # Use a different cache store in production. 56 | # config.cache_store = :mem_cache_store 57 | 58 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 59 | # config.action_controller.asset_host = "http://assets.example.com" 60 | 61 | # Ignore bad email addresses and do not raise email delivery errors. 62 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 63 | # config.action_mailer.raise_delivery_errors = false 64 | 65 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 66 | # the I18n.default_locale when a translation cannot be found). 67 | config.i18n.fallbacks = true 68 | 69 | # Send deprecation notices to registered listeners. 70 | config.active_support.deprecation = :notify 71 | 72 | # Disable automatic flushing of the log to improve performance. 73 | # config.autoflush_log = false 74 | 75 | # Use default logging formatter so that PID and timestamp are not suppressed. 76 | config.log_formatter = ::Logger::Formatter.new 77 | 78 | # Do not dump schema after migrations. 79 | config.active_record.dump_schema_after_migration = false 80 | end 81 | -------------------------------------------------------------------------------- /spec/dummy/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Dummy::Application.configure do 4 | # Settings specified here will take precedence over those in config/application.rb. 5 | 6 | # The test environment is used exclusively to run your application's 7 | # test suite. You never need to work with it otherwise. Remember that 8 | # your test database is "scratch space" for the test suite and is wiped 9 | # and recreated between test runs. Don't rely on the data there! 10 | config.cache_classes = true 11 | 12 | # Do not eager load code on boot. This avoids loading your whole application 13 | # just for the purpose of running a single test. If you are using a tool that 14 | # preloads Rails for running tests, you may have to set it to true. 15 | config.eager_load = true 16 | 17 | # Configure static asset server for tests with Cache-Control for performance. 18 | config.static_cache_control = 'public, max-age=3600' if Rails::VERSION::MAJOR < 5 19 | 20 | config.public_file_server.enabled = true if Rails::VERSION::MAJOR >= 5 21 | config.public_file_server.headers = { 'Cache-Control' => 'public, max-age=3600' } if Rails::VERSION::MAJOR >= 5 22 | 23 | # Show full error reports and disable caching. 24 | config.consider_all_requests_local = true 25 | config.action_controller.perform_caching = false 26 | 27 | # Raise exceptions instead of rendering exception templates. 28 | config.action_dispatch.show_exceptions = false 29 | 30 | # Disable request forgery protection in test environment. 31 | config.action_controller.allow_forgery_protection = false 32 | 33 | # Tell Action Mailer not to deliver emails to the real world. 34 | # The :test delivery method accumulates sent emails in the 35 | # ActionMailer::Base.deliveries array. 36 | #config.action_mailer.delivery_method = :test 37 | 38 | # Print deprecation notices to the stderr. 39 | config.active_support.deprecation = :stderr 40 | 41 | # Raises error for missing translations 42 | # config.action_view.raise_on_missing_translations = true 43 | end 44 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Version of your assets, change this if you want to expire all your assets. 6 | Rails.application.config.assets.version = '1.0' 7 | 8 | # Precompile additional assets. 9 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. 10 | # Rails.application.config.assets.precompile += %w( search.js ) 11 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 6 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 7 | 8 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 9 | # Rails.backtrace_cleaner.remove_silencers! 10 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/doorkeeper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Doorkeeper.configure do 4 | # Change the ORM that doorkeeper will use. 5 | # Currently supported options are :active_record, :mongoid2, :mongoid3, :mongo_mapper 6 | orm :active_record 7 | 8 | # This block will be called to check whether the resource owner is authenticated or not. 9 | resource_owner_authenticator do 10 | fail "Please configure doorkeeper resource_owner_authenticator block located in #{__FILE__}" 11 | # Put your resource owner authentication logic here. 12 | # Example implementation: 13 | # User.find_by_id(session[:user_id]) || redirect_to(new_user_session_url) 14 | end 15 | 16 | # If you want to restrict access to the web interface for adding oauth authorized applications, you need to declare the block below. 17 | # admin_authenticator do 18 | # # Put your admin authentication logic here. 19 | # # Example implementation: 20 | # Admin.find_by_id(session[:admin_id]) || redirect_to(new_admin_session_url) 21 | # end 22 | 23 | # Authorization Code expiration time (default 10 minutes). 24 | # authorization_code_expires_in 10.minutes 25 | 26 | # Access token expiration time (default 2 hours). 27 | # If you want to disable expiration, set this to nil. 28 | # access_token_expires_in 2.hours 29 | 30 | # Reuse access token for the same resource owner within an application (disabled by default) 31 | # Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/383 32 | # reuse_access_token 33 | 34 | # Issue access tokens with refresh token (disabled by default) 35 | # use_refresh_token 36 | 37 | # Provide support for an owner to be assigned to each registered application (disabled by default) 38 | # Optional parameter :confirmation => true (default false) if you want to enforce ownership of 39 | # a registered application 40 | # Note: you must also run the rails g doorkeeper:application_owner generator to provide the necessary support 41 | # enable_application_owner :confirmation => false 42 | 43 | # Define access token scopes for your provider 44 | # For more information go to 45 | # https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes 46 | default_scopes :public, :default 47 | optional_scopes :write, :update 48 | 49 | # Change the way client credentials are retrieved from the request object. 50 | # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then 51 | # falls back to the `:client_id` and `:client_secret` params from the `params` object. 52 | # Check out the wiki for more information on customization 53 | # client_credentials :from_basic, :from_params 54 | 55 | # Change the way access token is authenticated from the request object. 56 | # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then 57 | # falls back to the `:access_token` or `:bearer_token` params from the `params` object. 58 | # Check out the wiki for more information on customization 59 | # access_token_methods :from_bearer_authorization, :from_access_token_param, :from_bearer_param 60 | 61 | # Change the native redirect uri for client apps 62 | # When clients register with the following redirect uri, they won't be redirected to any server and the authorization code will be displayed within the provider 63 | # The value can be any string. Use nil to disable this feature. When disabled, clients must provide a valid URL 64 | # (Similar behaviour: https://developers.google.com/accounts/docs/OAuth2InstalledApp#choosingredirecturi) 65 | # 66 | # native_redirect_uri 'urn:ietf:wg:oauth:2.0:oob' 67 | 68 | # Specify what grant flows are enabled in array of Strings. The valid 69 | # strings and the flows they enable are: 70 | # 71 | # "authorization_code" => Authorization Code Grant Flow 72 | # "implicit" => Implicit Grant Flow 73 | # "password" => Resource Owner Password Credentials Grant Flow 74 | # "client_credentials" => Client Credentials Grant Flow 75 | # 76 | # If not specified, Doorkeeper enables all the four grant flows. 77 | # 78 | # grant_flows %w(authorization_code implicit password client_credentials) 79 | 80 | # Under some circumstances you might want to have applications auto-approved, 81 | # so that the user skips the authorization step. 82 | # For example if dealing with trusted a application. 83 | # skip_authorization do |resource_owner, client| 84 | # client.superapp? or resource_owner.admin? 85 | # end 86 | 87 | # WWW-Authenticate Realm (default "Doorkeeper"). 88 | # realm "Doorkeeper" 89 | 90 | # Allow dynamic query parameters (disabled by default) 91 | # Some applications require dynamic query parameters on their request_uri 92 | # set to true if you want this to be allowed 93 | # wildcard_redirect_uri false 94 | end 95 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Configure sensitive parameters which will be filtered from the log file. 6 | Rails.application.config.filter_parameters += [:password] 7 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Add new inflection rules using the following format. Inflections 6 | # are locale specific, and you may define rules for as many different 7 | # locales as you wish. All of these examples are active by default: 8 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 9 | # inflect.plural /^(ox)$/i, '\1en' 10 | # inflect.singular /^(ox)en/i, '\1' 11 | # inflect.irregular 'person', 'people' 12 | # inflect.uncountable %w( fish sheep ) 13 | # end 14 | 15 | # These inflection rules are supported but not enabled by default: 16 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 17 | # inflect.acronym 'RESTful' 18 | # end 19 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Add new mime types for use in respond_to blocks: 6 | # Mime::Type.register "text/richtext", :rtf 7 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/secret_token.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | if ENV['rails'][0].to_i > 4 && ENV['rails'][2].to_i >= 1 3 | Dummy::Application.config.secret_key_base = '23d9b4867e1370428ac81119ec43914b117ef4d95e8cb563c7813b22e1ac260688d0b11958eaae30f587712ac75ab852c76b91594e9f8a851fa5cd53ef2088a4' 4 | else 5 | Dummy::Application.config.secret_token = 'c65fd1ffec8275651d1fd06ec3a4914ba644bbeb87729594a3b35fb4b7ad4cccd298d77baf63f7a6513d437e5b95eef9637f9c37a9691c3ed41143d8b5f9a5ef' 6 | end 7 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | Rails.application.config.session_store :cookie_store, key: '_dummy_session' 6 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # This file contains settings for ActionController::ParamsWrapper which 6 | # is enabled by default. 7 | 8 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 9 | ActiveSupport.on_load(:action_controller) do 10 | wrap_parameters format: [:json] if respond_to?(:wrap_parameters) 11 | end 12 | 13 | # To enable root element in JSON for ActiveRecord objects. 14 | # ActiveSupport.on_load(:active_record) do 15 | # self.include_root_in_json = true 16 | # end 17 | -------------------------------------------------------------------------------- /spec/dummy/config/locales/doorkeeper.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | activerecord: 3 | errors: 4 | models: 5 | application: 6 | attributes: 7 | redirect_uri: 8 | fragment_present: 'cannot contain a fragment.' 9 | invalid_uri: 'must be a valid URI.' 10 | relative_uri: 'must be an absolute URI.' 11 | mongoid: 12 | errors: 13 | models: 14 | application: 15 | attributes: 16 | redirect_uri: 17 | fragment_present: 'cannot contain a fragment.' 18 | invalid_uri: 'must be a valid URI.' 19 | relative_uri: 'must be an absolute URI.' 20 | mongo_mapper: 21 | errors: 22 | models: 23 | application: 24 | attributes: 25 | redirect_uri: 26 | fragment_present: 'cannot contain a fragment.' 27 | invalid_uri: 'must be a valid URI.' 28 | relative_uri: 'must be an absolute URI.' 29 | doorkeeper: 30 | errors: 31 | messages: 32 | # Common error messages 33 | invalid_request: 'The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.' 34 | invalid_redirect_uri: 'The redirect uri included is not valid.' 35 | unauthorized_client: 'The client is not authorized to perform this request using this method.' 36 | access_denied: 'The resource owner or authorization server denied the request.' 37 | invalid_scope: 'The requested scope is invalid, unknown, or malformed.' 38 | server_error: 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.' 39 | temporarily_unavailable: 'The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server.' 40 | 41 | #configuration error messages 42 | credential_flow_not_configured: 'Resource Owner Password Credentials flow failed due to Doorkeeper.configure.resource_owner_from_credentials being unconfigured.' 43 | resource_owner_authenticator_not_configured: 'Resource Owner find failed due to Doorkeeper.configure.resource_owner_authenticator being unconfiged.' 44 | 45 | # Access grant errors 46 | unsupported_response_type: 'The authorization server does not support this response type.' 47 | 48 | # Access token errors 49 | invalid_client: 'Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method.' 50 | invalid_grant: 'The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.' 51 | unsupported_grant_type: 'The authorization grant type is not supported by the authorization server.' 52 | 53 | # Password Access token errors 54 | invalid_resource_owner: 'The provided resource owner credentials are not valid, or resource owner cannot be found' 55 | 56 | invalid_token: 57 | revoked: "The access token was revoked" 58 | expired: "The access token expired" 59 | unknown: "The access token is invalid" 60 | 61 | flash: 62 | applications: 63 | create: 64 | notice: 'Application created.' 65 | destroy: 66 | notice: 'Application deleted.' 67 | update: 68 | notice: 'Application updated.' 69 | authorized_applications: 70 | destroy: 71 | notice: 'Application revoked.' 72 | -------------------------------------------------------------------------------- /spec/dummy/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | -------------------------------------------------------------------------------- /spec/dummy/config/routes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Rails.application.routes.draw do 4 | use_doorkeeper 5 | mount Api::DefaultApiUnderTest => '/default_api' 6 | mount Api::SwaggerApiUnderTest => '/swagger_api' 7 | mount Api::ProtectedApiUnderTest => '/protected_api' 8 | end 9 | -------------------------------------------------------------------------------- /spec/dummy/config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rake secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | development: 14 | secret_key_base: f3331e00ed5d76db3737b2a0b4ccd79f19d858394ddc4995361813a18b24910af0b010475c7b8520ee35c04511e7d258b04b334131bfafdc119129b9c8d0cb83 15 | 16 | test: 17 | secret_key_base: c65fd1ffec8275651d1fd06ec3a4914ba644bbeb87729594a3b35fb4b7ad4cccd298d77baf63f7a6513d437e5b95eef9637f9c37a9691c3ed41143d8b5f9a5ef 18 | 19 | # Do not keep production secrets in the repository, 20 | # instead read values from the environment. 21 | production: 22 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 23 | -------------------------------------------------------------------------------- /spec/dummy/db/migrate/20140915153344_create_users.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateUsers < ActiveRecord::Migration 4 | def change 5 | create_table :users do |t| 6 | t.string :name 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/dummy/db/migrate/20140915160601_create_doorkeeper_tables.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class CreateDoorkeeperTables < ActiveRecord::Migration 4 | def change 5 | create_table :oauth_applications do |t| 6 | t.string :name, null: false 7 | t.string :uid, null: false 8 | t.string :secret, null: false 9 | t.text :redirect_uri, null: false 10 | t.timestamps 11 | end 12 | 13 | add_index :oauth_applications, :uid, unique: true 14 | 15 | create_table :oauth_access_grants do |t| 16 | t.integer :resource_owner_id, null: false 17 | t.integer :application_id, null: false 18 | t.string :token, null: false 19 | t.integer :expires_in, null: false 20 | t.text :redirect_uri, null: false 21 | t.datetime :created_at, null: false 22 | t.datetime :revoked_at 23 | t.string :scopes 24 | end 25 | 26 | add_index :oauth_access_grants, :token, unique: true 27 | 28 | create_table :oauth_access_tokens do |t| 29 | t.integer :resource_owner_id 30 | t.integer :application_id 31 | t.string :token, null: false 32 | t.string :refresh_token 33 | t.integer :expires_in 34 | t.datetime :revoked_at 35 | t.datetime :created_at, null: false 36 | t.string :scopes 37 | end 38 | 39 | add_index :oauth_access_tokens, :token, unique: true 40 | add_index :oauth_access_tokens, :resource_owner_id 41 | add_index :oauth_access_tokens, :refresh_token, unique: true 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/dummy/db/schema.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # encoding: UTF-8 4 | # This file is auto-generated from the current state of the database. Instead 5 | # of editing this file, please use the migrations feature of Active Record to 6 | # incrementally modify your database, and then regenerate this schema definition. 7 | # 8 | # Note that this schema.rb definition is the authoritative source for your 9 | # database schema. If you need to create the application database on another 10 | # system, you should be using db:schema:load, not running all the migrations 11 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 12 | # you'll amass, the slower it'll run and the greater likelihood for issues). 13 | # 14 | # It's strongly recommended that you check this file into your version control system. 15 | 16 | ActiveRecord::Schema.define(version: 20140915160601) do 17 | create_table 'oauth_access_grants', force: true do |t| 18 | t.integer 'resource_owner_id', null: false 19 | t.integer 'application_id', null: false 20 | t.string 'token', null: false 21 | t.integer 'expires_in', null: false 22 | t.text 'redirect_uri', null: false 23 | t.datetime 'created_at', null: false 24 | t.datetime 'revoked_at' 25 | t.string 'scopes' 26 | end 27 | 28 | add_index 'oauth_access_grants', ['token'], name: 'index_oauth_access_grants_on_token', unique: true 29 | 30 | create_table 'oauth_access_tokens', force: true do |t| 31 | t.integer 'resource_owner_id' 32 | t.integer 'application_id' 33 | t.string 'token', null: false 34 | t.string 'refresh_token' 35 | t.integer 'expires_in' 36 | t.datetime 'revoked_at' 37 | t.datetime 'created_at', null: false 38 | t.string 'scopes' 39 | end 40 | 41 | add_index 'oauth_access_tokens', ['refresh_token'], name: 'index_oauth_access_tokens_on_refresh_token', unique: true 42 | add_index 'oauth_access_tokens', ['resource_owner_id'], name: 'index_oauth_access_tokens_on_resource_owner_id' 43 | add_index 'oauth_access_tokens', ['token'], name: 'index_oauth_access_tokens_on_token', unique: true 44 | 45 | create_table 'oauth_applications', force: true do |t| 46 | t.string 'name', null: false 47 | t.string 'uid', null: false 48 | t.string 'secret', null: false 49 | t.text 'redirect_uri', null: false 50 | t.datetime 'created_at' 51 | t.datetime 'updated_at' 52 | t.boolean "confidential", default: true, null: false 53 | end 54 | 55 | add_index 'oauth_applications', ['uid'], name: 'index_oauth_applications_on_uid', unique: true 56 | 57 | create_table 'users', force: true do |t| 58 | t.string 'name' 59 | t.datetime 'created_at' 60 | t.datetime 'updated_at' 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /spec/dummy/lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antek-drzewiecki/wine_bouncer/c82b88f73c7adc43a8e89ca73f7e18952ec30de4/spec/dummy/lib/assets/.keep -------------------------------------------------------------------------------- /spec/dummy/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antek-drzewiecki/wine_bouncer/c82b88f73c7adc43a8e89ca73f7e18952ec30de4/spec/dummy/log/.keep -------------------------------------------------------------------------------- /spec/dummy/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The page you were looking for doesn't exist.

62 |

You may have mistyped the address or the page may have moved.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /spec/dummy/public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The change you wanted was rejected.

62 |

Maybe you tried to change something you didn't have access to.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /spec/dummy/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

We're sorry, but something went wrong.

62 |
63 |

If you are the application owner check the logs for more information.

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /spec/dummy/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antek-drzewiecki/wine_bouncer/c82b88f73c7adc43a8e89ca73f7e18952ec30de4/spec/dummy/public/favicon.ico -------------------------------------------------------------------------------- /spec/factories/access_token.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | FactoryBot.define do 4 | factory :access_token, class: Doorkeeper::AccessToken do 5 | sequence(:resource_owner_id) { |n| n } 6 | application 7 | expires_in { 2.hours } 8 | 9 | factory :clientless_access_token do 10 | application { nil } 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/factories/application.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | FactoryBot.define do 4 | factory :application, class: Doorkeeper::Application do 5 | sequence(:name) { |n| "Application #{n}" } 6 | redirect_uri { 'https://app.com/callback' } 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/factories/user.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | FactoryBot.define do 4 | factory :user do 5 | sequence(:name) { |n| "user-#{n}" } 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/intergration/oauth2_default_strategy_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails_helper' 4 | require 'json' 5 | 6 | describe Api::MountedDefaultApiUnderTest, type: :api do 7 | let(:user) { FactoryBot.create :user } 8 | let(:token) { FactoryBot.create :clientless_access_token, resource_owner_id: user.id, scopes: 'public' } 9 | let(:unscoped_token) { FactoryBot.create :clientless_access_token, resource_owner_id: user.id, scopes: '' } 10 | let(:custom_scope) { FactoryBot.create :clientless_access_token, resource_owner_id: user.id, scopes: 'custom_scope' } #not a default scope 11 | 12 | before(:example) do 13 | WineBouncer.configure do |c| 14 | c.auth_strategy = :default 15 | 16 | c.define_resource_owner do 17 | User.find(doorkeeper_access_token.resource_owner_id) if doorkeeper_access_token 18 | end 19 | end 20 | end 21 | 22 | context 'tokens and scopes' do 23 | it 'gives access when the token and scope are correct' do 24 | get '/default_api/protected', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" 25 | 26 | expect(last_response.status).to eq(200) 27 | json = JSON.parse(last_response.body) 28 | expect(json).to have_key('hello') 29 | end 30 | 31 | it 'gives access when tokens are correct and an non doorkeeper default scope is used.' do 32 | get '/default_api/oauth2_custom_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{custom_scope.token}" 33 | 34 | expect(last_response.status).to eq(200) 35 | json = JSON.parse(last_response.body) 36 | expect(json).to have_key('hello') 37 | expect(json['hello']).to eq('oauth2_custom_scope') 38 | end 39 | 40 | it 'raises an authentication error when the token is invalid' do 41 | expect { get '/default_api/protected', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}-invalid" }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError) 42 | end 43 | 44 | it 'raises an oauth authentication error when no token is given' do 45 | expect { get '/default_api/protected' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError) 46 | end 47 | 48 | it 'raises an auth forbidden authentication error when the user scope is not correct' do 49 | expect { get '/default_api/protected_with_private_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" }.to raise_exception(WineBouncer::Errors::OAuthForbiddenError) 50 | end 51 | end 52 | 53 | context 'unprotected endpoint' do 54 | it 'allows to call an unprotected endpoint without token' do 55 | get '/default_api/unprotected' 56 | 57 | expect(last_response.status).to eq(200) 58 | json = JSON.parse(last_response.body) 59 | 60 | expect(json).to have_key('hello') 61 | expect(json['hello']).to eq('unprotected world') 62 | end 63 | 64 | it 'allows to call an unprotected endpoint with token' do 65 | get '/default_api/unprotected', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" 66 | 67 | expect(last_response.status).to eq(200) 68 | json = JSON.parse(last_response.body) 69 | expect(json).to have_key('hello') 70 | expect(json['hello']).to eq('unprotected world') 71 | end 72 | end 73 | 74 | context 'protected_without_scopes' do 75 | it 'allows to call an protected endpoint without scopes' do 76 | get '/default_api/protected_without_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" 77 | 78 | expect(last_response.status).to eq(200) 79 | json = JSON.parse(last_response.body) 80 | expect(json).to have_key('hello') 81 | expect(json['hello']).to eq('protected unscoped world') 82 | end 83 | 84 | it 'raises an error when an protected endpoint without scopes is called without token ' do 85 | expect { get '/default_api/protected_without_scope' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError) 86 | end 87 | 88 | it 'raises an error because the user does not have the default scope' do 89 | expect { get '/default_api/protected_without_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{unscoped_token.token}" }.to raise_exception(WineBouncer::Errors::OAuthForbiddenError) 90 | end 91 | end 92 | 93 | context 'oauth2_dsl' do 94 | it 'allows to call an protected endpoint without scopes' do 95 | get '/default_api/oauth2_dsl', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" 96 | 97 | expect(last_response.status).to eq(200) 98 | json = JSON.parse(last_response.body) 99 | expect(json).to have_key('hello') 100 | expect(json['hello']).to eq('oauth2 dsl') 101 | end 102 | 103 | it 'raises an error when an protected endpoint without scopes is called without token ' do 104 | expect { get '/default_api/oauth2_dsl' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError) 105 | end 106 | 107 | context 'without parameters' do 108 | it 'accepts tokens with default scopes' do 109 | get '/swagger_api/oauth2_dsl_default_scopes', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" 110 | expect(last_response.status).to eq(200) 111 | json = JSON.parse(last_response.body) 112 | expect(json).to have_key('hello') 113 | expect(json['hello']).to eq('oauth dsl default scopes') 114 | end 115 | 116 | it 'raises an error when an protected endpoint without scopes is called without token ' do 117 | expect { get '/default_api/oauth2_dsl_default_scopes' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError) 118 | end 119 | 120 | it 'raises an error when token scopes are not default scopes ' do 121 | expect { get '/default_api/oauth2_dsl_default_scopes', nil, 'HTTP_AUTHORIZATION' => "Bearer #{custom_scope.token}" }.to raise_exception(WineBouncer::Errors::OAuthForbiddenError) 122 | end 123 | end 124 | 125 | context 'custom scopes' do 126 | it 'allows to call custom scopes' do 127 | get '/default_api/oauth2_dsl_custom_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{custom_scope.token}" 128 | expect(last_response.status).to eq(200) 129 | json = JSON.parse(last_response.body) 130 | expect(json).to have_key('hello') 131 | expect(json['hello']).to eq('oauth2 dsl custom scope') 132 | end 133 | 134 | it 'raises an error when an protected endpoint without scopes is called without token ' do 135 | expect { get '/default_api/oauth2_dsl_custom_scope' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError) 136 | end 137 | 138 | it 'raises an error when token scopes do not match' do 139 | expect { get '/default_api/oauth2_dsl_custom_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" }.to raise_exception(WineBouncer::Errors::OAuthForbiddenError) 140 | end 141 | end 142 | 143 | context 'oauth2_dsl_multiple_scopes' do 144 | it 'allows call on the first scope' do 145 | scope_token = FactoryBot.create :clientless_access_token, resource_owner_id: user.id, scopes: 'multiple' 146 | get '/default_api/oauth2_dsl_multiple_scopes', nil, 'HTTP_AUTHORIZATION' => "Bearer #{scope_token.token}" 147 | expect(last_response.status).to eq(200) 148 | json = JSON.parse(last_response.body) 149 | expect(json).to have_key('hello') 150 | expect(json['hello']).to eq('oauth2 dsl multiple scopes') 151 | end 152 | 153 | it 'allows call on the second scope' do 154 | scope_token = FactoryBot.create :clientless_access_token, resource_owner_id: user.id, scopes: 'scopes' 155 | get '/default_api/oauth2_dsl_multiple_scopes', nil, 'HTTP_AUTHORIZATION' => "Bearer #{scope_token.token}" 156 | expect(last_response.status).to eq(200) 157 | json = JSON.parse(last_response.body) 158 | expect(json).to have_key('hello') 159 | expect(json['hello']).to eq('oauth2 dsl multiple scopes') 160 | end 161 | 162 | it 'raises an error scope does not match any of the scopes' do 163 | expect { get '/default_api/oauth2_dsl_multiple_scopes' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError) 164 | end 165 | end 166 | end 167 | 168 | context 'not_described_world' do 169 | it 'allows to call an endpoint without description' do 170 | get '/default_api/not_described_world' 171 | expect(last_response.status).to eq(200) 172 | json = JSON.parse(last_response.body) 173 | expect(json).to have_key('hello') 174 | expect(json['hello']).to eq('non described world') 175 | end 176 | end 177 | 178 | context 'resource_owner' do 179 | it 'is available in the endpoint' do 180 | get '/default_api/protected_user', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" 181 | 182 | expect(last_response.status).to eq(200) 183 | json = JSON.parse(last_response.body) 184 | 185 | expect(json).to have_key('hello') 186 | expect(json['hello']).to eq(user.name) 187 | end 188 | end 189 | end 190 | -------------------------------------------------------------------------------- /spec/intergration/oauth2_protected_strategy_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails_helper' 4 | require 'json' 5 | 6 | describe Api::MountedProtectedApiUnderTest, type: :api do 7 | let(:user) { FactoryBot.create :user } 8 | let(:token) { FactoryBot.create :clientless_access_token, resource_owner_id: user.id, scopes: 'public' } 9 | let(:unscoped_token) { FactoryBot.create :clientless_access_token, resource_owner_id: user.id, scopes: '' } 10 | let(:custom_scope) { FactoryBot.create :clientless_access_token, resource_owner_id: user.id, scopes: 'custom_scope' } #not a default scope 11 | 12 | before(:example) do 13 | WineBouncer.configure do |c| 14 | c.auth_strategy = :protected 15 | 16 | c.define_resource_owner do 17 | User.find(doorkeeper_access_token.resource_owner_id) if doorkeeper_access_token 18 | end 19 | end 20 | end 21 | 22 | context 'when WineBouncer is disabled' do 23 | before :all do 24 | WineBouncer.configure do |c| 25 | c.disable { true } 26 | end 27 | end 28 | 29 | after :all do 30 | WineBouncer.configure do |c| 31 | c.disable { false } 32 | end 33 | end 34 | 35 | it 'allows request to protected resource without token' do 36 | get '/protected_api/protected' 37 | expect(last_response.status).to eq(200) 38 | json = JSON.parse(last_response.body) 39 | expect(json).to have_key('hello') 40 | end 41 | end 42 | 43 | context 'tokens and scopes' do 44 | it 'gives access when the token and scope are correct' do 45 | get '/protected_api/protected', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" 46 | 47 | expect(last_response.status).to eq(200) 48 | json = JSON.parse(last_response.body) 49 | expect(json).to have_key('hello') 50 | end 51 | 52 | it 'raises an authentication error when the token is invalid' do 53 | expect { get '/protected_api/protected', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}-invalid" }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError) 54 | end 55 | 56 | it 'raises an oauth authentication error when no token is given' do 57 | expect { get '/protected_api/protected' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError) 58 | end 59 | 60 | it 'raises an auth forbidden authentication error when the user scope is not correct' do 61 | expect { get '/protected_api/protected_with_private_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" }.to raise_exception(WineBouncer::Errors::OAuthForbiddenError) 62 | end 63 | end 64 | 65 | context 'unprotected endpoint' do 66 | it 'allows to call an unprotected endpoint without token' do 67 | get '/protected_api/unprotected' 68 | 69 | expect(last_response.status).to eq(200) 70 | json = JSON.parse(last_response.body) 71 | 72 | expect(json).to have_key('hello') 73 | expect(json['hello']).to eq('unprotected world') 74 | end 75 | 76 | it 'allows to call an unprotected endpoint with token' do 77 | get '/protected_api/unprotected', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" 78 | 79 | expect(last_response.status).to eq(200) 80 | json = JSON.parse(last_response.body) 81 | expect(json).to have_key('hello') 82 | expect(json['hello']).to eq('unprotected world') 83 | end 84 | end 85 | 86 | context 'protected_without_scopes' do 87 | it 'does not allow an unauthenticated user to call a protected endpoint' do 88 | expect { get '/protected_api/protected_without_scope' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError) 89 | end 90 | 91 | it 'allows to call an protected endpoint without scopes' do 92 | get '/protected_api/protected_without_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" 93 | 94 | expect(last_response.status).to eq(200) 95 | json = JSON.parse(last_response.body) 96 | expect(json).to have_key('hello') 97 | expect(json['hello']).to eq('protected unscoped world') 98 | end 99 | 100 | it 'raises an error when an protected endpoint without scopes is called without token ' do 101 | expect { get '/protected_api/protected_without_scope' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError) 102 | end 103 | 104 | it 'raises an error because the user does not have the default scope' do 105 | expect { get '/protected_api/protected_without_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{unscoped_token.token}" }.to raise_exception(WineBouncer::Errors::OAuthForbiddenError) 106 | end 107 | end 108 | 109 | context 'oauth2_dsl' do 110 | it 'allows to call an protected endpoint without scopes' do 111 | get '/protected_api/oauth2_dsl', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" 112 | 113 | expect(last_response.status).to eq(200) 114 | json = JSON.parse(last_response.body) 115 | expect(json).to have_key('hello') 116 | expect(json['hello']).to eq('oauth2_dsl') 117 | end 118 | 119 | it 'raises an error when an protected endpoint is called without token' do 120 | expect { get '/protected_api/oauth2_dsl' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError) 121 | end 122 | 123 | context 'without parameters' do 124 | it 'allows to call an endpoint with default scopes' do 125 | get '/protected_api/oauth2_protected_with_default_scopes', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" 126 | expect(last_response.status).to eq(200) 127 | json = JSON.parse(last_response.body) 128 | expect(json).to have_key('hello') 129 | expect(json['hello']).to eq('default oauth2 dsl') 130 | end 131 | 132 | it 'raises an error when an protected endpoint without scopes is called without token ' do 133 | expect { get '/protected_api/oauth2_protected_with_default_scopes' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError) 134 | end 135 | 136 | it 'raises an error when token scopes are not default scopes ' do 137 | expect { get '/protected_api/oauth2_protected_with_default_scopes', nil, 'HTTP_AUTHORIZATION' => "Bearer #{custom_scope.token}" }.to raise_exception(WineBouncer::Errors::OAuthForbiddenError) 138 | end 139 | end 140 | 141 | context 'custom scopes' do 142 | it 'protects endpoints with custom scopes' do 143 | get '/protected_api/oauth2_dsl_custom_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{custom_scope.token}" 144 | expect(last_response.status).to eq(200) 145 | json = JSON.parse(last_response.body) 146 | expect(json).to have_key('hello') 147 | expect(json['hello']).to eq('custom scope') 148 | end 149 | 150 | it 'raises an error when an protected endpoint without scopes is called without token ' do 151 | expect { get '/protected_api/oauth2_dsl_custom_scope' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError) 152 | end 153 | 154 | it 'raises an error when token scopes do not match' do 155 | expect { get '/protected_api/oauth2_dsl_custom_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" }.to raise_exception(WineBouncer::Errors::OAuthForbiddenError) 156 | end 157 | end 158 | 159 | context 'public endpoint' do 160 | it 'allows to call an unprotected endpoint without token' do 161 | get '/protected_api/unprotected_endpoint' 162 | expect(last_response.status).to eq(200) 163 | json = JSON.parse(last_response.body) 164 | expect(json).to have_key('hello') 165 | expect(json['hello']).to eq('public oauth2 dsl') 166 | end 167 | 168 | it 'allows requests with tokens to public endpoints with tokens' do 169 | expect { get '/protected_api/oauth2_protected_with_default_scopes', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" }.not_to raise_error 170 | end 171 | end 172 | end 173 | 174 | context 'not_described_world' do 175 | it 'authentication is required for a non described endpoint' do 176 | get '/protected_api/not_described_world', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" 177 | expect(last_response.status).to eq(200) 178 | json = JSON.parse(last_response.body) 179 | expect(json).to have_key('hello') 180 | expect(json['hello']).to eq('non described world') 181 | end 182 | 183 | it 'raises an error when an protected endpoint without scopes is called without token ' do 184 | expect { get '/protected_api/not_described_world' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError) 185 | end 186 | end 187 | 188 | context 'resource_owner' do 189 | it 'is available in the endpoint' do 190 | get '/protected_api/protected_user', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" 191 | 192 | expect(last_response.status).to eq(200) 193 | json = JSON.parse(last_response.body) 194 | 195 | expect(json).to have_key('hello') 196 | expect(json['hello']).to eq(user.name) 197 | end 198 | end 199 | end 200 | -------------------------------------------------------------------------------- /spec/intergration/oauth2_swagger_strategy_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails_helper' 4 | require 'json' 5 | 6 | describe Api::MountedSwaggerApiUnderTest, type: :api do 7 | let(:user) { FactoryBot.create :user } 8 | let(:token) { FactoryBot.create :clientless_access_token, resource_owner_id: user.id, scopes: 'public' } 9 | let(:unscoped_token) { FactoryBot.create :clientless_access_token, resource_owner_id: user.id, scopes: '' } 10 | let(:custom_scope) { FactoryBot.create :clientless_access_token, resource_owner_id: user.id, scopes: 'custom_scope' } #not a default scope 11 | 12 | before(:example) do 13 | WineBouncer.configure do |c| 14 | c.auth_strategy = :swagger 15 | 16 | c.define_resource_owner do 17 | User.find(doorkeeper_access_token.resource_owner_id) if doorkeeper_access_token 18 | end 19 | end 20 | end 21 | 22 | context 'tokens and scopes' do 23 | it 'gives access when the token and scope are correct' do 24 | get '/swagger_api/protected', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" 25 | 26 | expect(last_response.status).to eq(200) 27 | json = JSON.parse(last_response.body) 28 | expect(json).to have_key('hello') 29 | end 30 | 31 | it 'raises an authentication error when the token is invalid' do 32 | expect { get '/swagger_api/protected', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}-invalid" }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError) 33 | end 34 | 35 | it 'raises an oauth authentication error when no token is given' do 36 | expect { get '/swagger_api/protected' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError) 37 | end 38 | 39 | it 'raises an auth forbidden authentication error when the user scope is not correct' do 40 | expect { get '/swagger_api/protected_with_private_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" }.to raise_exception(WineBouncer::Errors::OAuthForbiddenError) 41 | end 42 | end 43 | 44 | context 'unprotected endpoint' do 45 | it 'allows to call an unprotected endpoint without token' do 46 | get '/swagger_api/unprotected' 47 | 48 | expect(last_response.status).to eq(200) 49 | json = JSON.parse(last_response.body) 50 | 51 | expect(json).to have_key('hello') 52 | expect(json['hello']).to eq('unprotected world') 53 | end 54 | 55 | it 'allows to call an unprotected endpoint with token' do 56 | get '/swagger_api/unprotected', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" 57 | 58 | expect(last_response.status).to eq(200) 59 | json = JSON.parse(last_response.body) 60 | expect(json).to have_key('hello') 61 | expect(json['hello']).to eq('unprotected world') 62 | end 63 | end 64 | 65 | context 'protected_without_scopes' do 66 | it 'allows to call an protected endpoint without scopes' do 67 | get '/swagger_api/protected_without_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" 68 | 69 | expect(last_response.status).to eq(200) 70 | json = JSON.parse(last_response.body) 71 | expect(json).to have_key('hello') 72 | expect(json['hello']).to eq('protected unscoped world') 73 | end 74 | 75 | it 'raises an error when an protected endpoint without scopes is called without token ' do 76 | expect { get '/swagger_api/protected_without_scope' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError) 77 | end 78 | 79 | it 'raises an error because the user does not have the default scope' do 80 | expect { get '/swagger_api/protected_without_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{unscoped_token.token}" }.to raise_exception(WineBouncer::Errors::OAuthForbiddenError) 81 | end 82 | end 83 | 84 | context 'oauth2 dsl' do 85 | it 'allows to call an protected endpoint without scopes' do 86 | get '/swagger_api/oauth2_dsl', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" 87 | 88 | expect(last_response.status).to eq(200) 89 | json = JSON.parse(last_response.body) 90 | expect(json).to have_key('hello') 91 | expect(json['hello']).to eq('oauth2_dsl') 92 | end 93 | 94 | it 'raises an error when an protected endpoint without scopes is called without token ' do 95 | expect { get '/swagger_api/oauth2_dsl' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError) 96 | end 97 | 98 | context 'without parameters' do 99 | it 'accepts tokens with default scopes' do 100 | get '/swagger_api/oauth2_dsl_default_scopes', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" 101 | expect(last_response.status).to eq(200) 102 | json = JSON.parse(last_response.body) 103 | expect(json).to have_key('hello') 104 | expect(json['hello']).to eq('oauth dsl default scopes') 105 | end 106 | 107 | it 'raises an error when an protected endpoint without scopes is called without token ' do 108 | expect { get '/swagger_api/oauth2_dsl_default_scopes' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError) 109 | end 110 | 111 | it 'raises an error when token scopes are not default scopes ' do 112 | expect { get '/swagger_api/oauth2_dsl_default_scopes', nil, 'HTTP_AUTHORIZATION' => "Bearer #{custom_scope.token}" }.to raise_exception(WineBouncer::Errors::OAuthForbiddenError) 113 | end 114 | end 115 | 116 | context 'custom scopes' do 117 | it 'accepts tokens with default scopes' do 118 | get '/swagger_api/oauth2_dsl_custom_scopes', nil, 'HTTP_AUTHORIZATION' => "Bearer #{custom_scope.token}" 119 | expect(last_response.status).to eq(200) 120 | json = JSON.parse(last_response.body) 121 | expect(json).to have_key('hello') 122 | expect(json['hello']).to eq('oauth dsl custom scopes') 123 | end 124 | 125 | it 'raises an error when an protected endpoint without scopes is called without token ' do 126 | expect { get '/swagger_api/oauth2_dsl_custom_scopes' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError) 127 | end 128 | 129 | it 'raises an error when token scopes do not match' do 130 | expect { get '/swagger_api/oauth2_dsl_custom_scopes', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" }.to raise_exception(WineBouncer::Errors::OAuthForbiddenError) 131 | end 132 | end 133 | end 134 | 135 | context 'not_described_world' do 136 | it 'allows to call an endpoint without description' do 137 | get '/swagger_api/not_described_world' 138 | expect(last_response.status).to eq(200) 139 | json = JSON.parse(last_response.body) 140 | expect(json).to have_key('hello') 141 | expect(json['hello']).to eq('non described world') 142 | end 143 | end 144 | 145 | context 'resource_owner' do 146 | it 'is available in the endpoint' do 147 | get '/swagger_api/protected_user', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" 148 | 149 | expect(last_response.status).to eq(200) 150 | json = JSON.parse(last_response.body) 151 | 152 | expect(json).to have_key('hello') 153 | expect(json['hello']).to eq(user.name) 154 | end 155 | end 156 | end 157 | -------------------------------------------------------------------------------- /spec/lib/generators/wine_bouncer/initializer_generator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'generator_spec' 4 | require 'rails_helper' 5 | require 'spec_helper' 6 | require 'generators/wine_bouncer/initializer_generator' 7 | 8 | describe WineBouncer::Generators::InitializerGenerator, type: :generator do 9 | destination '/tmp/wine_bouncer' 10 | 11 | before do 12 | prepare_destination 13 | run_generator 14 | end 15 | 16 | it 'creates a test initializer' do 17 | assert_file 'config/initializers/wine_bouncer.rb', /WineBouncer\.configure/ 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec/lib/wine_bouncer/auth_methods/auth_methods_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails_helper' 4 | 5 | describe ::WineBouncer::AuthMethods do 6 | let(:tested_class) do 7 | Class.new do 8 | include ::WineBouncer::AuthMethods 9 | end.new 10 | end 11 | let(:user) { FactoryBot.create(:user) } 12 | let(:token) { FactoryBot.create :clientless_access_token, resource_owner_id: user.id, scopes: 'public' } 13 | 14 | context 'doorkeeper_access_token' do 15 | it 'sets and gets a token' do 16 | tested_class.doorkeeper_access_token = token 17 | expect(tested_class.doorkeeper_access_token).to eq(token) 18 | end 19 | end 20 | 21 | context 'has_resource_owner?' do 22 | it 'gives true when the token has an resource owner' do 23 | tested_class.doorkeeper_access_token = token 24 | 25 | expect(tested_class.has_resource_owner?).to be true 26 | end 27 | 28 | it 'gives false when the class an no token' do 29 | expect(tested_class.has_resource_owner?).to be false 30 | end 31 | 32 | it 'gives false when the token has no resource owner' do 33 | token.resource_owner_id = nil 34 | tested_class.doorkeeper_access_token = token 35 | 36 | expect(tested_class.has_resource_owner?).to be false 37 | end 38 | end 39 | 40 | context 'has_doorkeeper_token?' do 41 | it 'returns true when the class has a token' do 42 | tested_class.doorkeeper_access_token = token 43 | expect(tested_class.has_doorkeeper_token?).to be true 44 | end 45 | 46 | it 'returns false when the class has no token' do 47 | expect(tested_class.has_doorkeeper_token?).to be false 48 | end 49 | end 50 | 51 | context 'client_credential_token?' do 52 | it 'return true if the doorkeeper token is aquired through client_credential authentication' do 53 | token.resource_owner_id = nil 54 | tested_class.doorkeeper_access_token = token 55 | expect(tested_class.client_credential_token?).to be true 56 | end 57 | 58 | it 'return false if no token is set' do 59 | token.resource_owner_id = nil 60 | tested_class.doorkeeper_access_token 61 | expect(tested_class.client_credential_token?).to be false 62 | end 63 | 64 | it 'return false if the token has a resource_owner' do 65 | token.resource_owner_id = 2 66 | tested_class.doorkeeper_access_token = token 67 | expect(tested_class.client_credential_token?).to be false 68 | end 69 | end 70 | 71 | context 'protected_endpoint?' do 72 | it 'when set true it returns true' do 73 | tested_class.protected_endpoint = true 74 | expect(tested_class.protected_endpoint?).to be true 75 | end 76 | 77 | it 'when set false it returns false' do 78 | tested_class.protected_endpoint = false 79 | expect(tested_class.protected_endpoint?).to be false 80 | end 81 | 82 | it 'defaults returns false if not set' do 83 | expect(tested_class.protected_endpoint?).to be false 84 | end 85 | end 86 | 87 | context 'resource_owner' do 88 | it 'runs the configured block' do 89 | result = 'called block' 90 | foo = proc { result } 91 | 92 | WineBouncer.configure do |c| 93 | c.auth_strategy = :default 94 | c.define_resource_owner(&foo) 95 | end 96 | 97 | expect(tested_class.resource_owner).to be(result) 98 | end 99 | 100 | it 'raises an argument error when the block is not configured' do 101 | WineBouncer.configuration = WineBouncer::Configuration.new 102 | expect { tested_class.resource_owner }.to raise_error(WineBouncer::Errors::UnconfiguredError) 103 | end 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /spec/lib/wine_bouncer/auth_strategies/default_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails_helper' 4 | require 'wine_bouncer/auth_strategies/default' 5 | 6 | describe ::WineBouncer::AuthStrategies::Default do 7 | subject(:klass) { ::WineBouncer::AuthStrategies::Default.new } 8 | 9 | let(:scopes) { %w(public private) } 10 | let(:scopes_hash) { { scopes: scopes } } 11 | let(:auth_context) { { route_options: { auth: scopes_hash } } } 12 | 13 | context 'endpoint_authorizations' do 14 | it 'returns the auth key of the authentication hash.' do 15 | context_double = double() 16 | allow(context_double).to receive(:options) { auth_context } 17 | klass.api_context = context_double 18 | expect(klass.send(:endpoint_authorizations)).to eq(scopes_hash) 19 | end 20 | 21 | it 'returns nil when the authentication key has no hash key.' do 22 | context_double = double() 23 | allow(context_double).to receive(:options) { { route_options: { some: scopes_hash } } } 24 | klass.api_context = context_double 25 | expect(klass.send(:endpoint_authorizations)).to eq(nil) 26 | end 27 | end 28 | 29 | context 'endpoint_protected?' do 30 | it 'returns true when the context has the auth key.' do 31 | context_double = double() 32 | allow(context_double).to receive(:options) { auth_context } 33 | klass.api_context = context_double 34 | expect(klass.endpoint_protected?).to eq(true) 35 | end 36 | 37 | it 'returns false when the context has no auth key.' do 38 | context_double = double() 39 | allow(context_double).to receive(:options) { { route_options: { some: scopes_hash } } } 40 | klass.api_context = context_double 41 | expect(klass.endpoint_protected?).to eq(false) 42 | end 43 | end 44 | 45 | context 'has_auth_scopes?' do 46 | it 'returns true when the context has the auth key.' do 47 | context_double = double() 48 | allow(context_double).to receive(:options) { auth_context } 49 | klass.api_context = context_double 50 | expect(klass.has_auth_scopes?).to eq(true) 51 | end 52 | 53 | it 'returns false when the context has no auth key.' do 54 | context_double = double() 55 | allow(context_double).to receive(:options) { { route_options: { some: scopes_hash } } } 56 | klass.api_context = context_double 57 | expect(klass.has_auth_scopes?).to eq(false) 58 | end 59 | 60 | it 'returns false when the auth scopes contain a blank scope array' do 61 | context_double = double() 62 | allow(context_double).to receive(:options) { { route_options: { auth: { scopes: [] } } } } 63 | klass.api_context = context_double 64 | expect(klass.has_auth_scopes?).to eq(false) 65 | end 66 | end 67 | 68 | context 'auth_scopes' do 69 | it 'returns an array of scopes' do 70 | context_double = double() 71 | allow(context_double).to receive(:options) { auth_context } 72 | klass.api_context = context_double 73 | expect(klass.auth_scopes).to eq([:public, :private]) 74 | end 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /spec/lib/wine_bouncer/auth_strategies/swagger_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rails_helper' 4 | require 'wine_bouncer/auth_strategies/swagger' 5 | 6 | describe ::WineBouncer::AuthStrategies::Swagger do 7 | subject(:klass) { ::WineBouncer::AuthStrategies::Swagger.new } 8 | 9 | let(:scopes) { [{ scope: 'private', description: 'anything' },{ scope: 'public', description: 'anything' }] } 10 | let(:scopes_map) { { oauth2: scopes } } 11 | let(:auth_context) { { route_options: { authorizations: scopes_map } } } 12 | 13 | context 'endpoint_authorizations' do 14 | it 'returns the auth key of the authentication hash.' do 15 | context_double = double() 16 | allow(context_double).to receive(:options) { auth_context } 17 | klass.api_context = context_double 18 | expect(klass.send(:endpoint_authorizations)).to eq(scopes_map) 19 | end 20 | 21 | it 'returns nil when the authentication key has no hash key.' do 22 | context_double = double() 23 | allow(context_double).to receive(:options) { { route_options: { some: scopes_map } } } 24 | klass.api_context = context_double 25 | expect(klass.send(:endpoint_authorizations)).to eq(nil) 26 | end 27 | end 28 | 29 | context 'has_authorizations?' do 30 | it 'returns true when it has an authentication hash.' do 31 | context_double = double() 32 | allow(context_double).to receive(:options) { auth_context } 33 | klass.api_context = context_double 34 | expect(klass.send(:has_authorizations?)).to eq(true) 35 | end 36 | 37 | it 'returns false when there is no authentication key.' do 38 | context_double = double() 39 | allow(context_double).to receive(:options) { { route_options: { some: scopes_map } } } 40 | klass.api_context = context_double 41 | expect(klass.send(:has_authorizations?)).to eq(false) 42 | end 43 | end 44 | 45 | context 'authorization_type_oauth2' do 46 | it 'returns the scopes.' do 47 | context_double = double() 48 | allow(context_double).to receive(:options) { auth_context } 49 | klass.api_context = context_double 50 | expect(klass.send(:authorization_type_oauth2)).to eq(scopes) 51 | end 52 | 53 | it 'returns nil when there is no oauth2 key.' do 54 | context_double = double() 55 | allow(context_double).to receive(:options) { { route_options: { authorizations: { no_oauth: scopes } } } } 56 | klass.api_context = context_double 57 | expect(klass.send(:authorization_type_oauth2)).to eq(nil) 58 | end 59 | end 60 | 61 | context 'endpoint_protected?' do 62 | it 'returns true when the context has the auth key.' do 63 | context_double = double() 64 | allow(context_double).to receive(:options) { auth_context } 65 | klass.api_context = context_double 66 | expect(klass.endpoint_protected?).to eq(true) 67 | end 68 | 69 | it 'returns false when the context has no auth key.' do 70 | context_double = double() 71 | allow(context_double).to receive(:options) { { route_options: { some: scopes_map } } } 72 | klass.api_context = context_double 73 | expect(klass.endpoint_protected?).to eq(false) 74 | end 75 | end 76 | 77 | context 'has_auth_scopes?' do 78 | it 'returns true when the context has the auth key.' do 79 | context_double = double() 80 | allow(context_double).to receive(:options) { auth_context } 81 | klass.api_context = context_double 82 | expect(klass.has_auth_scopes?).to eq(true) 83 | end 84 | 85 | it 'returns false when the context has no authorizations key.' do 86 | context_double = double() 87 | allow(context_double).to receive(:options) { { route_options: { some: scopes_map } } } 88 | klass.api_context = context_double 89 | expect(klass.has_auth_scopes?).to eq(false) 90 | end 91 | 92 | it 'returns false when the context has no oauth2 key.' do 93 | context_double = double() 94 | allow(context_double).to receive(:options) { { route_options: { authorizations: { no_oauth: scopes } } } } 95 | klass.api_context = context_double 96 | expect(klass.has_auth_scopes?).to eq(false) 97 | end 98 | 99 | it 'returns false when the auth scopes contain a blank scope array' do 100 | context_double = double() 101 | allow(context_double).to receive(:options) { { route_options: { authorizations: { oauth2: [] } } } } 102 | klass.api_context = context_double 103 | expect(klass.has_auth_scopes?).to eq(false) 104 | end 105 | end 106 | 107 | context 'auth_scopes' do 108 | it 'returns an array of scopes' do 109 | context_double = double() 110 | allow(context_double).to receive(:options) { auth_context } 111 | klass.api_context = context_double 112 | expect(klass.auth_scopes).to eq([:private, :public]) 113 | end 114 | end 115 | end 116 | -------------------------------------------------------------------------------- /spec/rails_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # This file is copied to spec/ when you run 'rails generate rspec:install' 4 | ENV['RAILS_ENV'] ||= 'test' 5 | ORM = (ENV['orm'] || :active_record).to_sym 6 | require 'spec_helper' 7 | require File.expand_path('../dummy/config/environment', __FILE__) 8 | require 'rspec/rails' 9 | require 'wine_bouncer' 10 | require 'factory_bot' 11 | require 'database_cleaner' 12 | 13 | # Add additional requires below this line. Rails is not loaded until this point! 14 | 15 | # Requires supporting ruby files with custom matchers and macros, etc, in 16 | # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are 17 | # run as spec files by default. This means that files in spec/support that end 18 | # in _spec.rb will both be required and run as specs, causing the specs to be 19 | # run twice. It is recommended that you do not name files matching this glob to 20 | # end with _spec.rb. You can configure this pattern with the --pattern 21 | # option on the command line or in ~/.rspec, .rspec or `.rspec-local`. 22 | # 23 | # The following line is provided for convenience purposes. It has the downside 24 | # of increasing the boot-up time by auto-requiring all files in the support 25 | # directory. Alternatively, in the individual `*_spec.rb` files, manually 26 | # require only the support files necessary. 27 | # 28 | # Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } 29 | 30 | # Checks for pending migrations before tests are run. 31 | # If you are not using ActiveRecord, you can remove this line. 32 | #ActiveRecord::Migration.maintain_test_schema! 33 | 34 | module ApiHelper 35 | include Rack::Test::Methods 36 | 37 | def app 38 | Rails.application 39 | end 40 | end 41 | 42 | def orm_name 43 | orm = Doorkeeper.configuration.orm 44 | [:mongoid2, :mongoid3, :mongoid4].include?(orm.to_sym) ? :mongoid : orm 45 | end 46 | 47 | require "shared/orm/#{orm_name}" 48 | 49 | FactoryBot.find_definitions 50 | 51 | RSpec.configure do |config| 52 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures 53 | #config.fixture_path = "#{::Rails.root}/spec/fixtures" 54 | 55 | # If you're not using ActiveRecord, or you'd prefer not to run each of your 56 | # examples within a transaction, remove the following line or assign false 57 | # instead of true. 58 | 59 | config.include FactoryBot::Syntax::Methods 60 | config.include ApiHelper, type: :api 61 | 62 | config.use_transactional_fixtures = false 63 | 64 | config.infer_spec_type_from_file_location! 65 | 66 | config.infer_base_class_for_anonymous_controllers = false 67 | 68 | config.before do 69 | DatabaseCleaner.start 70 | FactoryBot.lint 71 | # Doorkeeper.configure { orm :active_record } 72 | end 73 | 74 | config.after do 75 | DatabaseCleaner.clean 76 | end 77 | 78 | config.order = 'random' 79 | end 80 | -------------------------------------------------------------------------------- /spec/shared/orm/active_record.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | ActiveRecord::Migration.verbose = false 4 | load Rails.root + 'db/schema.rb' 5 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # This file was generated by the `rspec --init` command. Conventionally, all 4 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 5 | # The generated `.rspec` file contains `--require spec_helper` which will cause this 6 | # file to always be loaded, without a need to explicitly require it in any files. 7 | # 8 | # Given that it is always loaded, you are encouraged to keep this file as 9 | # light-weight as possible. Requiring heavyweight dependencies from this file 10 | # will add to the boot time of your test suite on EVERY test run, even for an 11 | # individual file that may not need all of that loaded. Instead, consider making 12 | # a separate helper file that requires the additional dependencies and performs 13 | # the additional setup, and require it from the spec files that actually need it. 14 | # 15 | # The `.rspec` file also contains a few flags that are not defaults but that 16 | # users commonly want. 17 | # 18 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 19 | 20 | require 'simplecov' 21 | SimpleCov.start 22 | 23 | RSpec.configure do |config| 24 | # rspec-expectations config goes here. You can use an alternate 25 | # assertion/expectation library such as wrong or the stdlib/minitest 26 | # assertions if you prefer. 27 | config.expect_with :rspec do |expectations| 28 | # This option will default to `true` in RSpec 4. It makes the `description` 29 | # and `failure_message` of custom matchers include text for helper methods 30 | # defined using `chain`, e.g.: 31 | # be_bigger_than(2).and_smaller_than(4).description 32 | # # => "be bigger than 2 and smaller than 4" 33 | # ...rather than: 34 | # # => "be bigger than 2" 35 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 36 | end 37 | 38 | # rspec-mocks config goes here. You can use an alternate test double 39 | # library (such as bogus or mocha) by changing the `mock_with` option here. 40 | config.mock_with :rspec do |mocks| 41 | # Prevents you from mocking or stubbing a method that does not exist on 42 | # a real object. This is generally recommended, and will default to 43 | # `true` in RSpec 4. 44 | mocks.verify_partial_doubles = true 45 | end 46 | 47 | # The settings below are suggested to provide a good initial experience 48 | # with RSpec, but feel free to customize to your heart's content. 49 | =begin 50 | # These two settings work together to allow you to limit a spec run 51 | # to individual examples or groups you care about by tagging them with 52 | # `:focus` metadata. When nothing is tagged with `:focus`, all examples 53 | # get run. 54 | config.filter_run :focus 55 | config.run_all_when_everything_filtered = true 56 | 57 | # Limits the available syntax to the non-monkey patched syntax that is recommended. 58 | # For more details, see: 59 | # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax 60 | # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ 61 | # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching 62 | config.disable_monkey_patching! 63 | 64 | # This setting enables warnings. It's recommended, but in some cases may 65 | # be too noisy due to issues in dependencies. 66 | config.warnings = true 67 | 68 | # Many RSpec users commonly either run the entire suite or an individual 69 | # file, and it's useful to allow more verbose output when running an 70 | # individual spec file. 71 | if config.files_to_run.one? 72 | # Use the documentation formatter for detailed output, 73 | # unless a formatter has already been configured 74 | # (e.g. via a command-line flag). 75 | config.default_formatter = 'doc' 76 | end 77 | 78 | # Print the 10 slowest examples and example groups at the 79 | # end of the spec run, to help surface which specs are running 80 | # particularly slow. 81 | config.profile_examples = 10 82 | 83 | # Run specs in random order to surface order dependencies. If you find an 84 | # order dependency and want to debug it, you can fix the order by providing 85 | # the seed, which is printed after each run. 86 | # --seed 1234 87 | config.order = :random 88 | 89 | # Seed global randomization in this process using the `--seed` CLI option. 90 | # Setting this allows you to use `--seed` to deterministically reproduce 91 | # test failures related to randomization by passing the same `--seed` value 92 | # as the one that triggered the failure. 93 | Kernel.srand config.seed 94 | =end 95 | end 96 | -------------------------------------------------------------------------------- /wine_bouncer.gemspec: -------------------------------------------------------------------------------- 1 | lib = File.expand_path('lib', __dir__) 2 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 3 | require 'wine_bouncer/version' 4 | 5 | Gem::Specification.new do |spec| 6 | spec.name = 'wine_bouncer' 7 | spec.version = WineBouncer::VERSION 8 | spec.authors = ['Antek Drzewiecki'] 9 | spec.email = ['a.drzewiecki@devsquare.nl'] 10 | spec.summary = %q{A Ruby gem that allows Oauth2 protection with Doorkeeper for Grape Api's} 11 | spec.homepage = 'https://github.com/antek-drzewiecki/wine_bouncer' 12 | spec.license = 'MIT' 13 | 14 | spec.files = `git ls-files -z`.split("\x0") 15 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 16 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 17 | spec.require_paths = ['lib'] 18 | 19 | spec.add_runtime_dependency 'grape', '>= 0.10' 20 | spec.add_runtime_dependency 'doorkeeper', '>= 1.4', '< 6.0' 21 | 22 | spec.add_development_dependency 'railties' 23 | spec.add_development_dependency 'bundler' 24 | spec.add_development_dependency 'rake', '~> 11.0' 25 | spec.add_development_dependency 'rspec-rails', '~> 3.5.0' 26 | spec.add_development_dependency 'factory_bot', '~> 4.8' 27 | spec.add_development_dependency 'generator_spec', '~> 0.9.0' 28 | spec.add_development_dependency 'sqlite3' 29 | spec.add_development_dependency 'database_cleaner', '~> 1.6' 30 | spec.add_development_dependency 'rubocop', '0.58.2' 31 | spec.add_development_dependency 'yard', '~> 0.9.16' 32 | spec.add_development_dependency 'simplecov' 33 | end 34 | --------------------------------------------------------------------------------