sorcery 0.13.0 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +1 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +5 -0
- data/.github/workflows/ruby.yml +49 -0
- data/.rubocop.yml +2 -2
- data/.rubocop_todo.yml +157 -1
- data/CHANGELOG.md +49 -0
- data/CODE_OF_CONDUCT.md +14 -0
- data/Gemfile +1 -1
- data/README.md +4 -4
- data/Rakefile +3 -1
- data/SECURITY.md +19 -0
- data/gemfiles/rails_52.gemfile +7 -0
- data/gemfiles/rails_60.gemfile +7 -0
- data/lib/generators/sorcery/helpers.rb +4 -0
- data/lib/generators/sorcery/templates/initializer.rb +111 -85
- data/lib/generators/sorcery/templates/migration/activity_logging.rb +5 -5
- data/lib/generators/sorcery/templates/migration/brute_force_protection.rb +4 -4
- data/lib/generators/sorcery/templates/migration/core.rb +4 -4
- data/lib/generators/sorcery/templates/migration/external.rb +3 -3
- data/lib/generators/sorcery/templates/migration/magic_login.rb +4 -4
- data/lib/generators/sorcery/templates/migration/remember_me.rb +3 -3
- data/lib/generators/sorcery/templates/migration/reset_password.rb +5 -5
- data/lib/generators/sorcery/templates/migration/user_activation.rb +4 -4
- data/lib/sorcery/adapters/active_record_adapter.rb +2 -2
- data/lib/sorcery/controller.rb +4 -1
- data/lib/sorcery/controller/config.rb +6 -6
- data/lib/sorcery/controller/submodules/activity_logging.rb +5 -3
- data/lib/sorcery/controller/submodules/external.rb +4 -1
- data/lib/sorcery/controller/submodules/http_basic_auth.rb +1 -0
- data/lib/sorcery/controller/submodules/remember_me.rb +2 -1
- data/lib/sorcery/controller/submodules/session_timeout.rb +2 -0
- data/lib/sorcery/crypto_providers/aes256.rb +1 -1
- data/lib/sorcery/crypto_providers/bcrypt.rb +6 -1
- data/lib/sorcery/engine.rb +7 -1
- data/lib/sorcery/model.rb +6 -5
- data/lib/sorcery/model/config.rb +5 -0
- data/lib/sorcery/model/submodules/magic_login.rb +7 -4
- data/lib/sorcery/model/submodules/reset_password.rb +6 -2
- data/lib/sorcery/providers/battlenet.rb +51 -0
- data/lib/sorcery/providers/discord.rb +52 -0
- data/lib/sorcery/providers/line.rb +63 -0
- data/lib/sorcery/providers/linkedin.rb +45 -36
- data/lib/sorcery/providers/vk.rb +1 -1
- data/lib/sorcery/version.rb +1 -1
- data/sorcery.gemspec +5 -6
- data/spec/controllers/controller_oauth2_spec.rb +41 -6
- data/spec/controllers/controller_oauth_spec.rb +6 -0
- data/spec/controllers/controller_remember_me_spec.rb +15 -12
- data/spec/controllers/controller_spec.rb +11 -1
- data/spec/providers/example_provider_spec.rb +17 -0
- data/spec/providers/example_spec.rb +17 -0
- data/spec/rails_app/app/assets/config/manifest.js +1 -0
- data/spec/rails_app/app/controllers/application_controller.rb +2 -0
- data/spec/rails_app/app/controllers/sorcery_controller.rb +69 -1
- data/spec/rails_app/config/routes.rb +10 -0
- data/spec/shared_examples/user_reset_password_shared_examples.rb +18 -2
- data/spec/shared_examples/user_shared_examples.rb +63 -0
- data/spec/sorcery_crypto_providers_spec.rb +60 -0
- data/spec/support/migration_helper.rb +12 -2
- data/spec/support/providers/example.rb +11 -0
- data/spec/support/providers/example_provider.rb +11 -0
- metadata +25 -15
- data/.travis.yml +0 -38
- data/gemfiles/active_record_rails_40.gemfile +0 -6
- data/gemfiles/active_record_rails_41.gemfile +0 -6
- data/gemfiles/active_record_rails_42.gemfile +0 -6
| @@ -150,6 +150,16 @@ describe SorceryController, type: :controller do | |
| 150 150 | 
             
                  end
         | 
| 151 151 | 
             
                end
         | 
| 152 152 |  | 
| 153 | 
            +
                it 'require_login before_action does not save the url for JSON requests' do
         | 
| 154 | 
            +
                  get :some_action, format: :json
         | 
| 155 | 
            +
                  expect(session[:return_to_url]).to be_nil
         | 
| 156 | 
            +
                end
         | 
| 157 | 
            +
             | 
| 158 | 
            +
                it 'require_login before_action does not save the url for XHR requests' do
         | 
| 159 | 
            +
                  get :some_action, xhr: true
         | 
| 160 | 
            +
                  expect(session[:return_to_url]).to be_nil
         | 
| 161 | 
            +
                end
         | 
| 162 | 
            +
             | 
| 153 163 | 
             
                it 'on successful login the user is redirected to the url he originally wanted' do
         | 
| 154 164 | 
             
                  session[:return_to_url] = 'http://test.host/some_action'
         | 
| 155 165 | 
             
                  post :test_return_to, params: { email: 'bla@bla.com', password: 'secret' }
         | 
| @@ -161,7 +171,7 @@ describe SorceryController, type: :controller do | |
| 161 171 | 
             
                # --- auto_login(user) ---
         | 
| 162 172 | 
             
                specify { should respond_to(:auto_login) }
         | 
| 163 173 |  | 
| 164 | 
            -
                it 'auto_login(user)  | 
| 174 | 
            +
                it 'auto_login(user) logs in a user instance' do
         | 
| 165 175 | 
             
                  session[:user_id] = nil
         | 
| 166 176 | 
             
                  subject.auto_login(user)
         | 
| 167 177 |  | 
| @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'spec_helper'
         | 
| 4 | 
            +
            require 'sorcery/providers/base'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            describe Sorcery::Providers::ExampleProvider do
         | 
| 7 | 
            +
              before(:all) do
         | 
| 8 | 
            +
                sorcery_reload!([:external])
         | 
| 9 | 
            +
                sorcery_controller_property_set(:external_providers, [:example_provider])
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              context 'fetching a multi-word custom provider' do
         | 
| 13 | 
            +
                it 'returns the provider' do
         | 
| 14 | 
            +
                  expect(Sorcery::Controller::Config.example_provider).to be_a(Sorcery::Providers::ExampleProvider)
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
            end
         | 
| @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'spec_helper'
         | 
| 4 | 
            +
            require 'sorcery/providers/base'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            describe Sorcery::Providers::Example do
         | 
| 7 | 
            +
              before(:all) do
         | 
| 8 | 
            +
                sorcery_reload!([:external])
         | 
| 9 | 
            +
                sorcery_controller_property_set(:external_providers, [:example])
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              context 'fetching a single-word custom provider' do
         | 
| 13 | 
            +
                it 'returns the provider' do
         | 
| 14 | 
            +
                  expect(Sorcery::Controller::Config.example).to be_a(Sorcery::Providers::Example)
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
            end
         | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            {}
         | 
| @@ -1,11 +1,12 @@ | |
| 1 1 | 
             
            require 'oauth'
         | 
| 2 2 |  | 
| 3 | 
            -
            class SorceryController <  | 
| 3 | 
            +
            class SorceryController < ApplicationController
         | 
| 4 4 | 
             
              protect_from_forgery
         | 
| 5 5 |  | 
| 6 6 | 
             
              before_action :require_login_from_http_basic, only: [:test_http_basic_auth]
         | 
| 7 7 | 
             
              before_action :require_login, only: %i[
         | 
| 8 8 | 
             
                test_logout
         | 
| 9 | 
            +
                test_logout_with_forget_me
         | 
| 9 10 | 
             
                test_logout_with_force_forget_me
         | 
| 10 11 | 
             
                test_should_be_logged_in
         | 
| 11 12 | 
             
                some_action
         | 
| @@ -50,6 +51,13 @@ class SorceryController < ActionController::Base | |
| 50 51 | 
             
                head :ok
         | 
| 51 52 | 
             
              end
         | 
| 52 53 |  | 
| 54 | 
            +
              def test_logout_with_forget_me
         | 
| 55 | 
            +
                remember_me!
         | 
| 56 | 
            +
                forget_me!
         | 
| 57 | 
            +
                logout
         | 
| 58 | 
            +
                head :ok
         | 
| 59 | 
            +
              end
         | 
| 60 | 
            +
             | 
| 53 61 | 
             
              def test_logout_with_force_forget_me
         | 
| 54 62 | 
             
                remember_me!
         | 
| 55 63 | 
             
                force_forget_me!
         | 
| @@ -142,6 +150,10 @@ class SorceryController < ActionController::Base | |
| 142 150 | 
             
                login_at(:slack)
         | 
| 143 151 | 
             
              end
         | 
| 144 152 |  | 
| 153 | 
            +
              def login_at_test_line
         | 
| 154 | 
            +
                login_at(:line)
         | 
| 155 | 
            +
              end
         | 
| 156 | 
            +
             | 
| 145 157 | 
             
              def login_at_test_with_state
         | 
| 146 158 | 
             
                login_at(:facebook, state: 'bla')
         | 
| 147 159 | 
             
              end
         | 
| @@ -154,6 +166,14 @@ class SorceryController < ActionController::Base | |
| 154 166 | 
             
                login_at(:auth0)
         | 
| 155 167 | 
             
              end
         | 
| 156 168 |  | 
| 169 | 
            +
              def login_at_test_discord
         | 
| 170 | 
            +
                login_at(:discord)
         | 
| 171 | 
            +
              end
         | 
| 172 | 
            +
             | 
| 173 | 
            +
              def login_at_test_battlenet
         | 
| 174 | 
            +
                login_at(:battlenet)
         | 
| 175 | 
            +
              end
         | 
| 176 | 
            +
             | 
| 157 177 | 
             
              def test_login_from_twitter
         | 
| 158 178 | 
             
                if (@user = login_from(:twitter))
         | 
| 159 179 | 
             
                  redirect_to 'bla', notice: 'Success!'
         | 
| @@ -268,6 +288,30 @@ class SorceryController < ActionController::Base | |
| 268 288 | 
             
                end
         | 
| 269 289 | 
             
              end
         | 
| 270 290 |  | 
| 291 | 
            +
              def test_login_from_line
         | 
| 292 | 
            +
                if @user = login_from(:line)
         | 
| 293 | 
            +
                  redirect_to 'bla', notice: 'Success!'
         | 
| 294 | 
            +
                else
         | 
| 295 | 
            +
                  redirect_to 'blu', alert: 'Failed!'
         | 
| 296 | 
            +
                end
         | 
| 297 | 
            +
              end
         | 
| 298 | 
            +
             | 
| 299 | 
            +
              def test_login_from_discord
         | 
| 300 | 
            +
                if (@user = login_from(:discord))
         | 
| 301 | 
            +
                  redirect_to 'bla', notice: 'Success!'
         | 
| 302 | 
            +
                else
         | 
| 303 | 
            +
                  redirect_to 'blu', alert: 'Failed!'
         | 
| 304 | 
            +
                end
         | 
| 305 | 
            +
              end
         | 
| 306 | 
            +
             | 
| 307 | 
            +
              def test_login_from_battlenet
         | 
| 308 | 
            +
                if (@user = login_from(:battlenet))
         | 
| 309 | 
            +
                  redirect_to 'bla', notice: 'Success!'
         | 
| 310 | 
            +
                else
         | 
| 311 | 
            +
                  redirect_to 'blu', alert: 'Failed!'
         | 
| 312 | 
            +
                end
         | 
| 313 | 
            +
              end
         | 
| 314 | 
            +
             | 
| 271 315 | 
             
              def test_return_to_with_external_twitter
         | 
| 272 316 | 
             
                if (@user = login_from(:twitter))
         | 
| 273 317 | 
             
                  redirect_back_or_to 'bla', notice: 'Success!'
         | 
| @@ -382,6 +426,30 @@ class SorceryController < ActionController::Base | |
| 382 426 | 
             
                end
         | 
| 383 427 | 
             
              end
         | 
| 384 428 |  | 
| 429 | 
            +
              def test_return_to_with_external_line
         | 
| 430 | 
            +
                if @user = login_from(:line)
         | 
| 431 | 
            +
                  redirect_back_or_to 'bla', notice: 'Success!'
         | 
| 432 | 
            +
                else
         | 
| 433 | 
            +
                  redirect_to 'blu', alert: 'Failed!'
         | 
| 434 | 
            +
                end
         | 
| 435 | 
            +
              end
         | 
| 436 | 
            +
             | 
| 437 | 
            +
              def test_return_to_with_external_discord
         | 
| 438 | 
            +
                if (@user = login_from(:discord))
         | 
| 439 | 
            +
                  redirect_back_or_to 'bla', notice: 'Success!'
         | 
| 440 | 
            +
                else
         | 
| 441 | 
            +
                  redirect_to 'blu', alert: 'Failed!'
         | 
| 442 | 
            +
                end
         | 
| 443 | 
            +
              end
         | 
| 444 | 
            +
             | 
| 445 | 
            +
              def test_return_to_with_external_battlenet
         | 
| 446 | 
            +
                if (@user = login_from(:battlenet))
         | 
| 447 | 
            +
                  redirect_back_or_to 'bla', notice: 'Success!'
         | 
| 448 | 
            +
                else
         | 
| 449 | 
            +
                  redirect_to 'blu', alert: 'Failed!'
         | 
| 450 | 
            +
                end
         | 
| 451 | 
            +
              end
         | 
| 452 | 
            +
             | 
| 385 453 | 
             
              def test_create_from_provider
         | 
| 386 454 | 
             
                provider = params[:provider]
         | 
| 387 455 | 
             
                login_from(provider)
         | 
| @@ -11,6 +11,7 @@ AppRoot::Application.routes.draw do | |
| 11 11 | 
             
                get :test_login_from_cookie
         | 
| 12 12 | 
             
                get :test_login_from
         | 
| 13 13 | 
             
                get :test_logout_with_remember
         | 
| 14 | 
            +
                get :test_logout_with_forget_me
         | 
| 14 15 | 
             
                get :test_logout_with_force_forget_me
         | 
| 15 16 | 
             
                get :test_invalidate_active_session
         | 
| 16 17 | 
             
                get :test_should_be_logged_in
         | 
| @@ -32,6 +33,9 @@ AppRoot::Application.routes.draw do | |
| 32 33 | 
             
                get :test_login_from_slack
         | 
| 33 34 | 
             
                get :test_login_from_instagram
         | 
| 34 35 | 
             
                get :test_login_from_auth0
         | 
| 36 | 
            +
                get :test_login_from_line
         | 
| 37 | 
            +
                get :test_login_from_discord
         | 
| 38 | 
            +
                get :test_login_from_battlenet
         | 
| 35 39 | 
             
                get :login_at_test
         | 
| 36 40 | 
             
                get :login_at_test_twitter
         | 
| 37 41 | 
             
                get :login_at_test_facebook
         | 
| @@ -47,6 +51,9 @@ AppRoot::Application.routes.draw do | |
| 47 51 | 
             
                get :login_at_test_slack
         | 
| 48 52 | 
             
                get :login_at_test_instagram
         | 
| 49 53 | 
             
                get :login_at_test_auth0
         | 
| 54 | 
            +
                get :login_at_test_line
         | 
| 55 | 
            +
                get :login_at_test_discord
         | 
| 56 | 
            +
                get :login_at_test_battlenet
         | 
| 50 57 | 
             
                get :test_return_to_with_external
         | 
| 51 58 | 
             
                get :test_return_to_with_external_twitter
         | 
| 52 59 | 
             
                get :test_return_to_with_external_facebook
         | 
| @@ -62,6 +69,9 @@ AppRoot::Application.routes.draw do | |
| 62 69 | 
             
                get :test_return_to_with_external_slack
         | 
| 63 70 | 
             
                get :test_return_to_with_external_instagram
         | 
| 64 71 | 
             
                get :test_return_to_with_external_auth0
         | 
| 72 | 
            +
                get :test_return_to_with_external_line
         | 
| 73 | 
            +
                get :test_return_to_with_external_discord
         | 
| 74 | 
            +
                get :test_return_to_with_external_battlenet
         | 
| 65 75 | 
             
                get :test_http_basic_auth
         | 
| 66 76 | 
             
                get :some_action_making_a_non_persisted_change_to_the_user
         | 
| 67 77 | 
             
                post :test_login_with_remember
         | 
| @@ -14,6 +14,8 @@ shared_examples_for 'rails_3_reset_password_model' do | |
| 14 14 | 
             
                context 'API' do
         | 
| 15 15 | 
             
                  specify { expect(user).to respond_to :deliver_reset_password_instructions! }
         | 
| 16 16 |  | 
| 17 | 
            +
                  specify { expect(user).to respond_to :change_password }
         | 
| 18 | 
            +
             | 
| 17 19 | 
             
                  specify { expect(user).to respond_to :change_password! }
         | 
| 18 20 |  | 
| 19 21 | 
             
                  it 'responds to .load_from_reset_password_token' do
         | 
| @@ -314,13 +316,27 @@ shared_examples_for 'rails_3_reset_password_model' do | |
| 314 316 | 
             
                  end
         | 
| 315 317 | 
             
                end
         | 
| 316 318 |  | 
| 317 | 
            -
                it 'when change_password! is called, deletes reset_password_token' do
         | 
| 319 | 
            +
                it 'when change_password! is called, deletes reset_password_token and calls #save!' do
         | 
| 318 320 | 
             
                  user.deliver_reset_password_instructions!
         | 
| 319 321 |  | 
| 320 322 | 
             
                  expect(user.reset_password_token).not_to be_nil
         | 
| 323 | 
            +
                  expect(user).to_not receive(:save)
         | 
| 324 | 
            +
                  expect(user).to receive(:save!)
         | 
| 321 325 |  | 
| 322 326 | 
             
                  user.change_password!('blabulsdf')
         | 
| 323 | 
            -
             | 
| 327 | 
            +
             | 
| 328 | 
            +
                  expect(user.reset_password_token).to be_nil
         | 
| 329 | 
            +
                end
         | 
| 330 | 
            +
             | 
| 331 | 
            +
                it 'when change_password is called, deletes reset_password_token and calls #save' do
         | 
| 332 | 
            +
                  new_password = 'blabulsdf'
         | 
| 333 | 
            +
             | 
| 334 | 
            +
                  user.deliver_reset_password_instructions!
         | 
| 335 | 
            +
                  expect(user.reset_password_token).not_to be_nil
         | 
| 336 | 
            +
                  expect(user).to_not receive(:save!)
         | 
| 337 | 
            +
                  expect(user).to receive(:save)
         | 
| 338 | 
            +
             | 
| 339 | 
            +
                  user.change_password(new_password)
         | 
| 324 340 |  | 
| 325 341 | 
             
                  expect(user.reset_password_token).to be_nil
         | 
| 326 342 | 
             
                end
         | 
| @@ -54,6 +54,13 @@ shared_examples_for 'rails_3_core_model' do | |
| 54 54 | 
             
                  expect(User.sorcery_config.custom_encryption_provider).to eq Array
         | 
| 55 55 | 
             
                end
         | 
| 56 56 |  | 
| 57 | 
            +
                it "enables configuration option 'pepper'" do
         | 
| 58 | 
            +
                  pepper = '*$%&%*++'
         | 
| 59 | 
            +
                  sorcery_model_property_set(:pepper, pepper)
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  expect(User.sorcery_config.pepper).to eq pepper
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
             | 
| 57 64 | 
             
                it "enables configuration option 'salt_join_token'" do
         | 
| 58 65 | 
             
                  salt_join_token = '--%%*&-'
         | 
| 59 66 | 
             
                  sorcery_model_property_set(:salt_join_token, salt_join_token)
         | 
| @@ -459,6 +466,14 @@ shared_examples_for 'rails_3_core_model' do | |
| 459 466 | 
             
                  expect(User.encrypt(@text)).to eq Sorcery::CryptoProviders::SHA512.encrypt(@text)
         | 
| 460 467 | 
             
                end
         | 
| 461 468 |  | 
| 469 | 
            +
                it 'if encryption algo is bcrypt it works' do
         | 
| 470 | 
            +
                  sorcery_model_property_set(:encryption_algorithm, :bcrypt)
         | 
| 471 | 
            +
             | 
| 472 | 
            +
                  # comparison is done using BCrypt::Password#==(raw_token), not by String#==
         | 
| 473 | 
            +
                  expect(User.encrypt(@text)).to be_an_instance_of BCrypt::Password
         | 
| 474 | 
            +
                  expect(User.encrypt(@text)).to eq @text
         | 
| 475 | 
            +
                end
         | 
| 476 | 
            +
             | 
| 462 477 | 
             
                it 'salt is random for each user and saved in db' do
         | 
| 463 478 | 
             
                  sorcery_model_property_set(:salt_attribute_name, :salt)
         | 
| 464 479 |  | 
| @@ -488,6 +503,54 @@ shared_examples_for 'rails_3_core_model' do | |
| 488 503 |  | 
| 489 504 | 
             
                  expect(user.crypted_password).to eq Sorcery::CryptoProviders::SHA512.encrypt('secret', user.salt)
         | 
| 490 505 | 
             
                end
         | 
| 506 | 
            +
             | 
| 507 | 
            +
                it 'if pepper is set uses it to encrypt' do
         | 
| 508 | 
            +
                  sorcery_model_property_set(:salt_attribute_name, :salt)
         | 
| 509 | 
            +
                  sorcery_model_property_set(:pepper, '++@^$')
         | 
| 510 | 
            +
                  sorcery_model_property_set(:encryption_algorithm, :bcrypt)
         | 
| 511 | 
            +
             | 
| 512 | 
            +
                  # password comparison is done using BCrypt::Password#==(raw_token), not String#==
         | 
| 513 | 
            +
                  bcrypt_password = BCrypt::Password.new(user.crypted_password)
         | 
| 514 | 
            +
                  allow(::BCrypt::Password).to receive(:create) do |token, options = {}|
         | 
| 515 | 
            +
                    # need to use common BCrypt's salt when genarating BCrypt::Password objects
         | 
| 516 | 
            +
                    # so that any generated password hashes can be compared each other
         | 
| 517 | 
            +
                    ::BCrypt::Engine.hash_secret(token, bcrypt_password.salt)
         | 
| 518 | 
            +
                  end
         | 
| 519 | 
            +
             | 
| 520 | 
            +
                  expect(user.crypted_password).not_to eq Sorcery::CryptoProviders::BCrypt.encrypt('secret')
         | 
| 521 | 
            +
             | 
| 522 | 
            +
                  Sorcery::CryptoProviders::BCrypt.pepper = ''
         | 
| 523 | 
            +
             | 
| 524 | 
            +
                  expect(user.crypted_password).not_to eq Sorcery::CryptoProviders::BCrypt.encrypt('secret', user.salt)
         | 
| 525 | 
            +
             | 
| 526 | 
            +
                  Sorcery::CryptoProviders::BCrypt.pepper = User.sorcery_config.pepper
         | 
| 527 | 
            +
             | 
| 528 | 
            +
                  expect(user.crypted_password).to eq Sorcery::CryptoProviders::BCrypt.encrypt('secret', user.salt)
         | 
| 529 | 
            +
                end
         | 
| 530 | 
            +
             | 
| 531 | 
            +
                it 'if pepper is empty string (default) does not use pepper to encrypt' do
         | 
| 532 | 
            +
                  sorcery_model_property_set(:salt_attribute_name, :salt)
         | 
| 533 | 
            +
                  sorcery_model_property_set(:pepper, '')
         | 
| 534 | 
            +
                  sorcery_model_property_set(:encryption_algorithm, :bcrypt)
         | 
| 535 | 
            +
             | 
| 536 | 
            +
                  # password comparison is done using BCrypt::Password#==(raw_token), not String#==
         | 
| 537 | 
            +
                  bcrypt_password = BCrypt::Password.new(user.crypted_password)
         | 
| 538 | 
            +
                  allow(::BCrypt::Password).to receive(:create) do |token, options = {}|
         | 
| 539 | 
            +
                    # need to use common BCrypt's salt when genarating BCrypt::Password objects
         | 
| 540 | 
            +
                    # so that any generated password hashes can be compared each other
         | 
| 541 | 
            +
                    ::BCrypt::Engine.hash_secret(token, bcrypt_password.salt)
         | 
| 542 | 
            +
                  end
         | 
| 543 | 
            +
             | 
| 544 | 
            +
                  expect(user.crypted_password).not_to eq Sorcery::CryptoProviders::BCrypt.encrypt('secret')
         | 
| 545 | 
            +
             | 
| 546 | 
            +
                  Sorcery::CryptoProviders::BCrypt.pepper = 'some_pepper'
         | 
| 547 | 
            +
             | 
| 548 | 
            +
                  expect(user.crypted_password).not_to eq Sorcery::CryptoProviders::BCrypt.encrypt('secret', user.salt)
         | 
| 549 | 
            +
             | 
| 550 | 
            +
                  Sorcery::CryptoProviders::BCrypt.pepper = User.sorcery_config.pepper
         | 
| 551 | 
            +
             | 
| 552 | 
            +
                  expect(user.crypted_password).to eq Sorcery::CryptoProviders::BCrypt.encrypt('secret', user.salt)
         | 
| 553 | 
            +
                end
         | 
| 491 554 | 
             
              end
         | 
| 492 555 |  | 
| 493 556 | 
             
              describe 'ORM adapter' do
         | 
| @@ -148,6 +148,7 @@ describe 'Crypto Providers wrappers' do | |
| 148 148 | 
             
                before(:all) do
         | 
| 149 149 | 
             
                  Sorcery::CryptoProviders::BCrypt.cost = 1
         | 
| 150 150 | 
             
                  @digest = BCrypt::Password.create('Noam Ben-Ari', cost: Sorcery::CryptoProviders::BCrypt.cost)
         | 
| 151 | 
            +
                  @tokens = %w[password gq18WBnJYNh2arkC1kgH]
         | 
| 151 152 | 
             
                end
         | 
| 152 153 |  | 
| 153 154 | 
             
                after(:each) do
         | 
| @@ -181,5 +182,64 @@ describe 'Crypto Providers wrappers' do | |
| 181 182 | 
             
                  # stubbed in Sorcery::TestHelpers::Internal
         | 
| 182 183 | 
             
                  expect(Sorcery::CryptoProviders::BCrypt.cost).to eq 1
         | 
| 183 184 | 
             
                end
         | 
| 185 | 
            +
             | 
| 186 | 
            +
                it 'matches token encrypted with salt from upstream' do
         | 
| 187 | 
            +
                  # note: actual comparison is done by BCrypt::Password#==(raw_token)
         | 
| 188 | 
            +
                  expect(Sorcery::CryptoProviders::BCrypt.encrypt(@tokens)).to eq @tokens.flatten.join
         | 
| 189 | 
            +
                end
         | 
| 190 | 
            +
             | 
| 191 | 
            +
                it 'respond_to?(:pepper) returns true' do
         | 
| 192 | 
            +
                  expect(Sorcery::CryptoProviders::BCrypt.respond_to?(:pepper)).to be true
         | 
| 193 | 
            +
                end
         | 
| 194 | 
            +
             | 
| 195 | 
            +
                context 'when pepper is provided' do
         | 
| 196 | 
            +
                  before(:each) do
         | 
| 197 | 
            +
                    Sorcery::CryptoProviders::BCrypt.pepper = 'pepper'
         | 
| 198 | 
            +
                    @digest = Sorcery::CryptoProviders::BCrypt.encrypt(@tokens) # a BCrypt::Password object
         | 
| 199 | 
            +
                  end
         | 
| 200 | 
            +
             | 
| 201 | 
            +
                  it 'matches token encrypted with salt and pepper from upstream' do
         | 
| 202 | 
            +
                    # note: actual comparison is done by BCrypt::Password#==(raw_token)
         | 
| 203 | 
            +
                    expect(@digest).to eq @tokens.flatten.join.concat('pepper')
         | 
| 204 | 
            +
                  end
         | 
| 205 | 
            +
             | 
| 206 | 
            +
                  it 'matches? returns true when matches' do
         | 
| 207 | 
            +
                    expect(Sorcery::CryptoProviders::BCrypt.matches?(@digest, *@tokens)).to be true
         | 
| 208 | 
            +
                  end
         | 
| 209 | 
            +
             | 
| 210 | 
            +
                  it 'matches? returns false when pepper is replaced with empty string' do
         | 
| 211 | 
            +
                    Sorcery::CryptoProviders::BCrypt.pepper = ''
         | 
| 212 | 
            +
                    expect(Sorcery::CryptoProviders::BCrypt.matches?(@digest, *@tokens)).to be false
         | 
| 213 | 
            +
                  end
         | 
| 214 | 
            +
             | 
| 215 | 
            +
                  it 'matches? returns false when no match' do
         | 
| 216 | 
            +
                    expect(Sorcery::CryptoProviders::BCrypt.matches?(@digest, 'a_random_incorrect_password')).to be false
         | 
| 217 | 
            +
                  end
         | 
| 218 | 
            +
                end
         | 
| 219 | 
            +
             | 
| 220 | 
            +
                context "when pepper is an empty string (default)" do
         | 
| 221 | 
            +
                  before(:each) do
         | 
| 222 | 
            +
                    Sorcery::CryptoProviders::BCrypt.pepper = ''
         | 
| 223 | 
            +
                    @digest = Sorcery::CryptoProviders::BCrypt.encrypt(@tokens) # a BCrypt::Password object
         | 
| 224 | 
            +
                  end
         | 
| 225 | 
            +
             | 
| 226 | 
            +
                  # make sure the default pepper '' does nothing
         | 
| 227 | 
            +
                  it 'matches token encrypted with salt only (without pepper)' do
         | 
| 228 | 
            +
                    expect(@digest).to eq @tokens.flatten.join # keep consistency with the older versions of #join_token
         | 
| 229 | 
            +
                  end
         | 
| 230 | 
            +
             | 
| 231 | 
            +
                  it 'matches? returns true when matches' do
         | 
| 232 | 
            +
                    expect(Sorcery::CryptoProviders::BCrypt.matches?(@digest, *@tokens)).to be true
         | 
| 233 | 
            +
                  end
         | 
| 234 | 
            +
             | 
| 235 | 
            +
                  it 'matches? returns false when pepper has changed' do
         | 
| 236 | 
            +
                    Sorcery::CryptoProviders::BCrypt.pepper = 'a new pepper'
         | 
| 237 | 
            +
                    expect(Sorcery::CryptoProviders::BCrypt.matches?(@digest, *@tokens)).to be false
         | 
| 238 | 
            +
                  end
         | 
| 239 | 
            +
             | 
| 240 | 
            +
                  it 'matches? returns false when no match' do
         | 
| 241 | 
            +
                    expect(Sorcery::CryptoProviders::BCrypt.matches?(@digest, 'a_random_incorrect_password')).to be false
         | 
| 242 | 
            +
                  end
         | 
| 243 | 
            +
                end
         | 
| 184 244 | 
             
              end
         | 
| 185 245 | 
             
            end
         | 
| @@ -1,7 +1,9 @@ | |
| 1 1 | 
             
            class MigrationHelper
         | 
| 2 2 | 
             
              class << self
         | 
| 3 3 | 
             
                def migrate(path)
         | 
| 4 | 
            -
                  if ActiveRecord.version >= Gem::Version.new(' | 
| 4 | 
            +
                  if ActiveRecord.version >= Gem::Version.new('6.0.0')
         | 
| 5 | 
            +
                    ActiveRecord::MigrationContext.new(path, schema_migration).migrate
         | 
| 6 | 
            +
                  elsif ActiveRecord.version >= Gem::Version.new('5.2.0')
         | 
| 5 7 | 
             
                    ActiveRecord::MigrationContext.new(path).migrate
         | 
| 6 8 | 
             
                  else
         | 
| 7 9 | 
             
                    ActiveRecord::Migrator.migrate(path)
         | 
| @@ -9,11 +11,19 @@ class MigrationHelper | |
| 9 11 | 
             
                end
         | 
| 10 12 |  | 
| 11 13 | 
             
                def rollback(path)
         | 
| 12 | 
            -
                  if ActiveRecord.version >= Gem::Version.new(' | 
| 14 | 
            +
                  if ActiveRecord.version >= Gem::Version.new('6.0.0')
         | 
| 15 | 
            +
                    ActiveRecord::MigrationContext.new(path, schema_migration).rollback
         | 
| 16 | 
            +
                  elsif ActiveRecord.version >= Gem::Version.new('5.2.0')
         | 
| 13 17 | 
             
                    ActiveRecord::MigrationContext.new(path).rollback
         | 
| 14 18 | 
             
                  else
         | 
| 15 19 | 
             
                    ActiveRecord::Migrator.rollback(path)
         | 
| 16 20 | 
             
                  end
         | 
| 17 21 | 
             
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                private
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def schema_migration
         | 
| 26 | 
            +
                  ActiveRecord::Base.connection.schema_migration
         | 
| 27 | 
            +
                end
         | 
| 18 28 | 
             
              end
         | 
| 19 29 | 
             
            end
         |