From e8f10bf94b338beae4bc62a22fb6b7c730ea852b Mon Sep 17 00:00:00 2001 From: bealking Date: Mon, 25 Sep 2017 14:43:19 +0800 Subject: [PATCH 001/112] add qq provider --- .../sorcery/templates/initializer.rb | 4 + lib/sorcery/controller/submodules/external.rb | 1 + lib/sorcery/providers/qq.rb | 76 +++++++++++++++++++ spec/controllers/controller_oauth2_spec.rb | 19 +++-- .../app/controllers/sorcery_controller.rb | 20 +++++ spec/rails_app/config/routes.rb | 3 + 6 files changed, 118 insertions(+), 5 deletions(-) create mode 100644 lib/sorcery/providers/qq.rb diff --git a/lib/generators/sorcery/templates/initializer.rb b/lib/generators/sorcery/templates/initializer.rb index fa7f939b..5f53f050 100644 --- a/lib/generators/sorcery/templates/initializer.rb +++ b/lib/generators/sorcery/templates/initializer.rb @@ -126,6 +126,10 @@ # config.wechat.secret = "" # config.wechat.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=wechat" # + # config.wechat.key = "" + # config.wechat.secret = "" + # config.wechat.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=qq" + # # config.google.key = "" # config.google.secret = "" # config.google.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=google" diff --git a/lib/sorcery/controller/submodules/external.rb b/lib/sorcery/controller/submodules/external.rb index 72ec2678..fd4e8630 100644 --- a/lib/sorcery/controller/submodules/external.rb +++ b/lib/sorcery/controller/submodules/external.rb @@ -22,6 +22,7 @@ def self.included(base) require 'sorcery/providers/paypal' require 'sorcery/providers/slack' require 'sorcery/providers/wechat' + require 'sorcery/providers/qq' require 'sorcery/providers/microsoft' Config.module_eval do diff --git a/lib/sorcery/providers/qq.rb b/lib/sorcery/providers/qq.rb new file mode 100644 index 00000000..c4b9f733 --- /dev/null +++ b/lib/sorcery/providers/qq.rb @@ -0,0 +1,76 @@ +module Sorcery + module Providers + # This class adds support for OAuth with graph.qq.com. + # + class Qq < Base + include Protocols::Oauth2 + + attr_accessor :auth_url, :scope, :token_url, :user_info_path, :openid_path + + def initialize + super + + @scope = 'get_user_info' + @auth_url = 'https://graph.qq.com/oauth2.0/authorize' + @openid_path = 'https://graph.qq.com/oauth2.0/me' + @user_info_path = 'https://graph.qq.com/user/get_user_info' + @token_url = 'https://graph.qq.com/oauth2.0/token' + @state = SecureRandom.hex(16) + @grant_type = 'authorization_code' + end + + def authorize_url(options = {}) + oauth_params = { + response_type: 'code', + client_id: @key, + redirect_uri: @callback_url, + state: @state, + scope: @scope + } + "#{options[:authorize_url]}?#{oauth_params.to_query}#qq_redirect" + end + + def get_user_hash(access_token) + openid_response = access_token.get(openid_path, params: { + access_token: access_token.token, + }) + + openid = JSON.parse(openid_response.body).fetch('openid') + + info_response = access_token.get(user_info_path, params: { + access_token: access_token.token, + openid: openid + }) + + {}.tap do |h| + h[:user_info] = JSON.parse(info_response.body) + h[:uid] = openid + end + end + + def get_access_token(args, options = {}) + client = build_client(options) + client.auth_code.get_token( + args[:code], + { client_id: @key, client_secret: @secret, grant_type: @grant_type, redirect_uri: @callback_url}, + options + ) + end + + def login_url(_params, _session) + authorize_url authorize_url: auth_url + end + + def process_callback(params, _session) + args = {}.tap do |a| + a[:code] = params[:code] if params[:code].present? + end + + get_access_token( + args, + token_url: token_url, + ) + end + end + end +end diff --git a/spec/controllers/controller_oauth2_spec.rb b/spec/controllers/controller_oauth2_spec.rb index 653564d8..402f3908 100644 --- a/spec/controllers/controller_oauth2_spec.rb +++ b/spec/controllers/controller_oauth2_spec.rb @@ -151,7 +151,7 @@ expect(flash[:notice]).to eq 'Success!' end - [:github, :google, :liveid, :vk, :salesforce, :paypal, :slack, :wechat, :microsoft].each do |provider| + [:github, :google, :liveid, :vk, :salesforce, :paypal, :slack, :wechat, :microsoft, :qq].each do |provider| describe "with #{provider}" do it 'login_at redirects correctly' do get :"login_at_test_#{provider}" @@ -201,7 +201,7 @@ end sorcery_reload!([:user_activation,:external], :user_activation_mailer => ::SorceryMailer) - sorcery_controller_property_set(:external_providers, [:facebook, :github, :google, :liveid, :vk, :salesforce, :paypal, :slack, :wechat, :microsoft]) + sorcery_controller_property_set(:external_providers, [:facebook, :github, :google, :liveid, :vk, :salesforce, :paypal, :slack, :wechat, :microsoft, :qq]) # TODO: refactor sorcery_controller_external_property_set(:facebook, :key, "eYVNBjBDi33aa9GkA3w") @@ -234,6 +234,9 @@ sorcery_controller_external_property_set(:microsoft, :key, "eYVNBjBDi33aa9GkA3w") sorcery_controller_external_property_set(:microsoft, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") sorcery_controller_external_property_set(:microsoft, :callback_url, "http://blabla.com") + sorcery_controller_external_property_set(:qq, :key, "eYVNBjBDi33aa9GkA3w") + sorcery_controller_external_property_set(:qq, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") + sorcery_controller_external_property_set(:qq, :callback_url, "http://blabla.com") end after(:all) do @@ -263,7 +266,7 @@ expect(ActionMailer::Base.deliveries.size).to eq old_size end - [:github, :google, :liveid, :vk, :salesforce, :paypal, :wechat, :microsoft].each do |provider| + [:github, :google, :liveid, :vk, :salesforce, :paypal, :wechat, :microsoft, :qq].each do |provider| it "does not send activation email to external users (#{provider})" do old_size = ActionMailer::Base.deliveries.size create_new_external_user provider @@ -403,6 +406,8 @@ def stub_all_oauth2_requests! }, # response for wechat auth 'unionid' => '123', + # response for qq auth + 'openid' => '123' }.to_json } allow(access_token).to receive(:get) { response } allow(access_token).to receive(:token) { '187041a618229fdaf16613e96e1caabc1e86e46bbfad228de41520e63fe45873684c365a14417289599f3' } @@ -412,7 +417,7 @@ def stub_all_oauth2_requests! end def set_external_property - sorcery_controller_property_set(:external_providers, [:facebook, :github, :google, :liveid, :vk, :salesforce, :paypal, :slack, :wechat, :microsoft]) + sorcery_controller_property_set(:external_providers, [:facebook, :github, :google, :liveid, :vk, :salesforce, :paypal, :slack, :wechat, :microsoft, :qq]) sorcery_controller_external_property_set(:facebook, :key, "eYVNBjBDi33aa9GkA3w") sorcery_controller_external_property_set(:facebook, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") sorcery_controller_external_property_set(:facebook, :callback_url, "http://blabla.com") @@ -443,6 +448,9 @@ def set_external_property sorcery_controller_external_property_set(:microsoft, :key, "eYVNBjBDi33aa9GkA3w") sorcery_controller_external_property_set(:microsoft, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") sorcery_controller_external_property_set(:microsoft, :callback_url, "http://blabla.com") + sorcery_controller_external_property_set(:qq, :key, "eYVNBjBDi33aa9GkA3w") + sorcery_controller_external_property_set(:qq, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") + sorcery_controller_external_property_set(:qq, :callback_url, "http://blabla.com") end def provider_url(provider) @@ -455,7 +463,8 @@ def provider_url(provider) salesforce: "https://login.salesforce.com/services/oauth2/authorize?client_id=#{::Sorcery::Controller::Config.salesforce.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope#{'=' + ::Sorcery::Controller::Config.salesforce.scope unless ::Sorcery::Controller::Config.salesforce.scope.nil?}&state", slack: "https://slack.com/oauth/authorize?client_id=#{::Sorcery::Controller::Config.slack.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=identity.basic%2C+identity.email&state", wechat: "https://open.weixin.qq.com/connect/qrconnect?appid=#{::Sorcery::Controller::Config.wechat.key}&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=snsapi_login&state=#wechat_redirect", - microsoft: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=#{::Sorcery::Controller::Config.microsoft.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=openid+email+https%3A%2F%2Fgraph.microsoft.com%2FUser.Read&state" + microsoft: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=#{::Sorcery::Controller::Config.microsoft.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=openid+email+https%3A%2F%2Fgraph.microsoft.com%2FUser.Read&state", + qq: "https://graph.qq.com/oauth2.0/authorize?client_id=#{::Sorcery::Controller::Config.wechat.key}&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=get_user_info&state=#qq_redirect", }[provider] end end diff --git a/spec/rails_app/app/controllers/sorcery_controller.rb b/spec/rails_app/app/controllers/sorcery_controller.rb index 1d916fe5..11adc3cc 100644 --- a/spec/rails_app/app/controllers/sorcery_controller.rb +++ b/spec/rails_app/app/controllers/sorcery_controller.rb @@ -108,6 +108,10 @@ def login_at_test_microsoft login_at(:microsoft) end + def login_at_test_qq + login_at(:qq) + end + def login_at_test_google login_at(:google) end @@ -178,6 +182,14 @@ def test_login_from_wechat end end + def test_login_from_qq + if @user = login_from(:qq) + redirect_to 'bla', notice: 'Success!' + else + redirect_to 'blu', alert: 'Failed!' + end + end + def test_login_from_microsoft if @user = login_from(:microsoft) redirect_to 'bla', notice: 'Success!' @@ -292,6 +304,14 @@ def test_return_to_with_external_microsoft end end + def test_return_to_with_external_qq + if @user = login_from(:qq) + redirect_back_or_to 'bla', notice: 'Success!' + else + redirect_to 'blu', alert: 'Failed!' + end + end + def test_return_to_with_external_google if @user = login_from(:google) redirect_back_or_to 'bla', notice: 'Success!' diff --git a/spec/rails_app/config/routes.rb b/spec/rails_app/config/routes.rb index 712d7f6a..b313f025 100644 --- a/spec/rails_app/config/routes.rb +++ b/spec/rails_app/config/routes.rb @@ -22,6 +22,7 @@ get :test_login_from_github get :test_login_from_paypal get :test_login_from_wechat + get :test_login_from_qq get :test_login_from_microsoft get :test_login_from_google get :test_login_from_liveid @@ -35,6 +36,7 @@ get :login_at_test_github get :login_at_test_paypal get :login_at_test_wechat + get :login_at_test_qq get :login_at_test_microsoft get :login_at_test_google get :login_at_test_liveid @@ -48,6 +50,7 @@ get :test_return_to_with_external_github get :test_return_to_with_external_paypal get :test_return_to_with_external_wechat + get :test_return_to_with_external_qq get :test_return_to_with_external_microsoft get :test_return_to_with_external_google get :test_return_to_with_external_liveid From fd95db0fcf89562c848b38f279318f47e18bddd0 Mon Sep 17 00:00:00 2001 From: bealking Date: Sat, 7 Oct 2017 23:11:54 +0800 Subject: [PATCH 002/112] bug fix --- lib/generators/sorcery/templates/initializer.rb | 7 ++++--- lib/sorcery/providers/qq.rb | 15 +++++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/generators/sorcery/templates/initializer.rb b/lib/generators/sorcery/templates/initializer.rb index 5f53f050..96552065 100644 --- a/lib/generators/sorcery/templates/initializer.rb +++ b/lib/generators/sorcery/templates/initializer.rb @@ -126,9 +126,10 @@ # config.wechat.secret = "" # config.wechat.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=wechat" # - # config.wechat.key = "" - # config.wechat.secret = "" - # config.wechat.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=qq" + # config.qq.key = "" + # config.qq.secret = "" + # config.qq.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=qq" + # config.qq.user_info_mapping = {:username => "nickname"} # # config.google.key = "" # config.google.secret = "" diff --git a/lib/sorcery/providers/qq.rb b/lib/sorcery/providers/qq.rb index c4b9f733..64c0ca17 100644 --- a/lib/sorcery/providers/qq.rb +++ b/lib/sorcery/providers/qq.rb @@ -5,6 +5,7 @@ module Providers class Qq < Base include Protocols::Oauth2 + attr_reader :parse attr_accessor :auth_url, :scope, :token_url, :user_info_path, :openid_path def initialize @@ -15,8 +16,8 @@ def initialize @openid_path = 'https://graph.qq.com/oauth2.0/me' @user_info_path = 'https://graph.qq.com/user/get_user_info' @token_url = 'https://graph.qq.com/oauth2.0/token' + @parse = :query @state = SecureRandom.hex(16) - @grant_type = 'authorization_code' end def authorize_url(options = {}) @@ -32,27 +33,29 @@ def authorize_url(options = {}) def get_user_hash(access_token) openid_response = access_token.get(openid_path, params: { - access_token: access_token.token, + access_token: access_token.token }) - openid = JSON.parse(openid_response.body).fetch('openid') + openid = openid_response.body.match(/"openid":"(\w{3,32})"/) || [nil, ''] info_response = access_token.get(user_info_path, params: { access_token: access_token.token, - openid: openid + oauth_consumer_key: @key, + openid: openid[1] }) {}.tap do |h| h[:user_info] = JSON.parse(info_response.body) - h[:uid] = openid + h[:uid] = openid[1] end end def get_access_token(args, options = {}) client = build_client(options) + client.auth_code.get_token( args[:code], - { client_id: @key, client_secret: @secret, grant_type: @grant_type, redirect_uri: @callback_url}, + { client_id: @key, client_secret: @secret, redirect_uri: @callback_url, parse: @parse}, options ) end From e2ca09500b93c918d8c68a62cbb12a054fbd7de8 Mon Sep 17 00:00:00 2001 From: 284km Date: Tue, 24 Oct 2017 20:15:49 +0900 Subject: [PATCH 003/112] CI against Ruby 2.2.8/2.3.5/2.4.2 --- .travis.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 381b868e..0087e2c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,9 @@ language: ruby rvm: - jruby - - 2.2.6 - - 2.3.3 - - 2.4.0 + - 2.2.8 + - 2.3.5 + - 2.4.2 env: global: @@ -27,19 +27,19 @@ matrix: - rvm: jruby exclude: - - rvm: 2.2.6 + - rvm: 2.2.8 gemfile: gemfiles/active_record-rails40.gemfile - - rvm: 2.3.3 + - rvm: 2.3.5 gemfile: gemfiles/active_record-rails40.gemfile - - rvm: 2.4.0 + - rvm: 2.4.2 gemfile: gemfiles/active_record-rails40.gemfile - - rvm: 2.4.0 + - rvm: 2.4.2 gemfile: gemfiles/active_record-rails41.gemfile - - rvm: 2.4.0 + - rvm: 2.4.2 gemfile: gemfiles/active_record-rails42.gemfile - rvm: jruby From aaa62a66ea45024fd0fa36616a673463f271cb8a Mon Sep 17 00:00:00 2001 From: "Bud (Mugur) Chirica" Date: Tue, 28 Nov 2017 03:44:32 +0000 Subject: [PATCH 004/112] Update minimum required Ruby version for Rails 5 (#85) --- sorcery.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sorcery.gemspec b/sorcery.gemspec index 644ce0ce..4bdfa7a8 100644 --- a/sorcery.gemspec +++ b/sorcery.gemspec @@ -18,7 +18,7 @@ Gem::Specification.new do |s| s.licenses = ['MIT'] - s.required_ruby_version = '>= 2.0.0' + s.required_ruby_version = '>= 2.2.2' s.add_dependency 'oauth', '~> 0.4', '>= 0.4.4' s.add_dependency 'oauth2', '~> 1.0', '>= 0.8.0' From da323591918daca653e7c43f66009a1cd2a9c991 Mon Sep 17 00:00:00 2001 From: Chase Gilliam Date: Mon, 27 Nov 2017 22:45:41 -0500 Subject: [PATCH 005/112] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6553ae0b..4cdb47a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## HEAD +* Updated Required Ruby version to 2.2 [#85](https://github.com/Sorcery/sorcery/pull/85) * Add configuration for token randomness [#67](https://github.com/Sorcery/sorcery/pull/67) * Add facebook user_info_path option to initializer.rb [#63](https://github.com/Sorcery/sorcery/pull/63) * Add new function: `build_from` (allows building a user instance from OAuth without saving) [#54](https://github.com/Sorcery/sorcery/pull/54) From fc8f12853e6623f324b6201118ee8765aa001f33 Mon Sep 17 00:00:00 2001 From: Ivan Takarlikov Date: Tue, 28 Nov 2017 10:48:39 +0700 Subject: [PATCH 006/112] add new ArgumentError for not defined user_class in config (#82) * add new ArgumentError for not defined user_class in config * updated grammar --- lib/sorcery/controller.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/sorcery/controller.rb b/lib/sorcery/controller.rb index 8b3010d1..50738851 100644 --- a/lib/sorcery/controller.rb +++ b/lib/sorcery/controller.rb @@ -155,6 +155,8 @@ def after_logout!(user) def user_class @user_class ||= Config.user_class.to_s.constantize + rescue NameError + raise ArgumentError, 'You have incorrectly defined user_class or have forgotten to define it in intitializer file (config.user_class = \'User\').' end end end From 7672938f3bc2e679b8716cc9ddbf7cae4ac41773 Mon Sep 17 00:00:00 2001 From: Chase Gilliam Date: Mon, 27 Nov 2017 22:49:12 -0500 Subject: [PATCH 007/112] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cdb47a8..7fe35cd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog ## HEAD - +* Added a new ArgumentError for not defined user_class in config [#82](https://github.com/Sorcery/sorcery/pull/82) * Updated Required Ruby version to 2.2 [#85](https://github.com/Sorcery/sorcery/pull/85) * Add configuration for token randomness [#67](https://github.com/Sorcery/sorcery/pull/67) * Add facebook user_info_path option to initializer.rb [#63](https://github.com/Sorcery/sorcery/pull/63) From 320628878339e217b048a9ca175070c5f6d6dbd0 Mon Sep 17 00:00:00 2001 From: Rostyslav Diachok Date: Tue, 28 Nov 2017 17:25:43 +0200 Subject: [PATCH 008/112] Allow user to be loaded from other source when session expires. fix #89 (#94) --- lib/sorcery/controller/submodules/session_timeout.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sorcery/controller/submodules/session_timeout.rb b/lib/sorcery/controller/submodules/session_timeout.rb index 79185c4b..e5b6924d 100644 --- a/lib/sorcery/controller/submodules/session_timeout.rb +++ b/lib/sorcery/controller/submodules/session_timeout.rb @@ -39,7 +39,7 @@ def validate_session session_to_use = Config.session_timeout_from_last_action ? session[:last_action_time] : session[:login_time] if session_to_use && sorcery_session_expired?(session_to_use.to_time) reset_sorcery_session - @current_user = nil + remove_instance_variable :@current_user if defined? @current_user else session[:last_action_time] = Time.now.in_time_zone end From dc1c34f2b34baf3154e2c129a4e8ce08d9411e8c Mon Sep 17 00:00:00 2001 From: Chase Gilliam Date: Tue, 28 Nov 2017 10:26:38 -0500 Subject: [PATCH 009/112] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fe35cd2..e236d3a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog ## HEAD +* Allow user to be loaded from other source when session expires. fix #89 [#94](https://github.com/Sorcery/sorcery/pull/94) * Added a new ArgumentError for not defined user_class in config [#82](https://github.com/Sorcery/sorcery/pull/82) * Updated Required Ruby version to 2.2 [#85](https://github.com/Sorcery/sorcery/pull/85) * Add configuration for token randomness [#67](https://github.com/Sorcery/sorcery/pull/67) From 958d62d0e81c770b4777252d6defa665586a8705 Mon Sep 17 00:00:00 2001 From: Vladimir Dementyev Date: Wed, 29 Nov 2017 00:03:04 +0300 Subject: [PATCH 010/112] Set user.stretches to 1 in test env by default (#81) Most users use BCrypt and don't change defaults, which means that the default value would be 10. That makes tests run slowly. Setting the number of stretches in test env to 1 can decrease the test suite up to 2x times (although BCrypt minimal cost is 4, we can use 1 for SHA algorithms). --- lib/generators/sorcery/templates/initializer.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/generators/sorcery/templates/initializer.rb b/lib/generators/sorcery/templates/initializer.rb index a1338253..bc835478 100644 --- a/lib/generators/sorcery/templates/initializer.rb +++ b/lib/generators/sorcery/templates/initializer.rb @@ -232,9 +232,9 @@ # user.salt_attribute_name = # how many times to apply encryption to the password. - # Default: `nil` + # Default: 1 in test env, `nil` otherwise # - # user.stretches = + user.stretches = 1 if Rails.env.test? # encryption key used to encrypt reversible encryptions such as AES256. # WARNING: If used for users' passwords, changing this key will leave passwords undecryptable! From 172659f59085313f3c65885a1565cf8a86bf8e13 Mon Sep 17 00:00:00 2001 From: Chase Gilliam Date: Tue, 28 Nov 2017 16:03:53 -0500 Subject: [PATCH 011/112] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e236d3a3..9baa3367 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog ## HEAD +* Set user.stretches to 1 in test env by default [#81](https://github.com/Sorcery/sorcery/pull/81) * Allow user to be loaded from other source when session expires. fix #89 [#94](https://github.com/Sorcery/sorcery/pull/94) * Added a new ArgumentError for not defined user_class in config [#82](https://github.com/Sorcery/sorcery/pull/82) * Updated Required Ruby version to 2.2 [#85](https://github.com/Sorcery/sorcery/pull/85) From 5f05584edc81cd4773a7c64251f755cc821ab981 Mon Sep 17 00:00:00 2001 From: ebi Date: Wed, 29 Nov 2017 13:17:42 +0900 Subject: [PATCH 012/112] Add tests to the magic login submodule (#95) * Add magic login feature * Use `.nil?` instead of `== nil` * Prepare for setting up database - create a migration file for spec - create a spec to be run and a shared_expmple file * Add configration tests change the default of `@magic_login_mailer_disabled` into true because the default breaks the tests * Change the configuration key, magic_login_mailer into magic_login_mailer_class * Add specs of `.generate_magic_login_token` * Add specs of `.clear_magic_login_token` * Add faliure case specs of `.magic_login_email` * Add success case specs of `.magic_login_email` * Refactoring: split the success case of the `.generate_magic_login_token` spec into two * Fix posix compliance offence: No newline at end of file --- .../sorcery/templates/initializer.rb | 50 ++++++ .../templates/migration/magic_login.rb | 9 ++ lib/sorcery.rb | 1 + lib/sorcery/model/submodules/magic_login.rb | 134 ++++++++++++++++ spec/active_record/user_magic_login_spec.rb | 15 ++ spec/rails_app/app/mailers/sorcery_mailer.rb | 7 + .../sorcery_mailer/magic_login_email.html.erb | 13 ++ .../sorcery_mailer/magic_login_email.text.erb | 6 + ...20170924151831_add_magic_login_to_users.rb | 17 ++ .../user_magic_login_shared_examples.rb | 150 ++++++++++++++++++ 10 files changed, 402 insertions(+) create mode 100644 lib/generators/sorcery/templates/migration/magic_login.rb create mode 100644 lib/sorcery/model/submodules/magic_login.rb create mode 100644 spec/active_record/user_magic_login_spec.rb create mode 100644 spec/rails_app/app/views/sorcery_mailer/magic_login_email.html.erb create mode 100644 spec/rails_app/app/views/sorcery_mailer/magic_login_email.text.erb create mode 100644 spec/rails_app/db/migrate/magic_login/20170924151831_add_magic_login_to_users.rb create mode 100644 spec/shared_examples/user_magic_login_shared_examples.rb diff --git a/lib/generators/sorcery/templates/initializer.rb b/lib/generators/sorcery/templates/initializer.rb index bc835478..36df69de 100644 --- a/lib/generators/sorcery/templates/initializer.rb +++ b/lib/generators/sorcery/templates/initializer.rb @@ -366,6 +366,56 @@ # # user.reset_password_time_between_emails = + # -- magic_login -- + # magic login code attribute name. + # Default: `:magic_login_token` + # + # user.magic_login_token_attribute_name = + + + # expires at attribute name. + # Default: `:magic_login_token_expires_at` + # + # user.magic_login_token_expires_at_attribute_name = + + + # when was email sent, used for hammering protection. + # Default: `:magic_login_email_sent_at` + # + # user.magic_login_email_sent_at_attribute_name = + + + # mailer class. Needed. + # Default: `nil` + # + # user.magic_login_mailer_class = + + + # magic login email method on your mailer class. + # Default: `:magic_login_email` + # + # user.magic_login_email_method_name = + + + # when true sorcery will not automatically + # email magic login details and allow you to + # manually handle how and when email is sent + # Default: `true` + # + # user.magic_login_mailer_disabled = + + + # how many seconds before the request expires. nil for never expires. + # Default: `nil` + # + # user.magic_login_expiration_period = + + + # hammering protection, how long in seconds to wait before allowing another email to be sent. + # Default: `5 * 60` + # + # user.magic_login_time_between_emails = + # -- brute_force_protection -- # Failed logins attribute name. # Default: `:failed_logins_count` diff --git a/lib/generators/sorcery/templates/migration/magic_login.rb b/lib/generators/sorcery/templates/migration/magic_login.rb new file mode 100644 index 00000000..a1a4082f --- /dev/null +++ b/lib/generators/sorcery/templates/migration/magic_login.rb @@ -0,0 +1,9 @@ +class SorceryMagicLogin < ActiveRecord::Migration + def change + add_column :<%= model_class_name.tableize %>, :magic_login_token, :string, :default => nil + add_column :<%= model_class_name.tableize %>, :magic_login_token_expires_at, :datetime, :default => nil + add_column :<%= model_class_name.tableize %>, :magic_login_email_sent_at, :datetime, :default => nil + + add_index :<%= model_class_name.tableize %>, :magic_login_token + end +end diff --git a/lib/sorcery.rb b/lib/sorcery.rb index e0df9312..d70266cf 100644 --- a/lib/sorcery.rb +++ b/lib/sorcery.rb @@ -18,6 +18,7 @@ module Submodules require 'sorcery/model/submodules/activity_logging' require 'sorcery/model/submodules/brute_force_protection' require 'sorcery/model/submodules/external' + require 'sorcery/model/submodules/magic_login' end end diff --git a/lib/sorcery/model/submodules/magic_login.rb b/lib/sorcery/model/submodules/magic_login.rb new file mode 100644 index 00000000..b1e534cc --- /dev/null +++ b/lib/sorcery/model/submodules/magic_login.rb @@ -0,0 +1,134 @@ +module Sorcery + module Model + module Submodules + # This submodule adds the ability to login via email without password. + # When the user requests an email is sent to him with a url. + # The url includes a token, which is also saved with the user's record in the db. + # The token has configurable expiration. + # When the user clicks the url in the email, providing the token has not yet expired, + # he will be able to login. + # + # When using this submodule, supplying a mailer is mandatory. + module MagicLogin + def self.included(base) + base.sorcery_config.class_eval do + attr_accessor :magic_login_token_attribute_name, # magic login code attribute name. + :magic_login_token_expires_at_attribute_name, # expires at attribute name. + :magic_login_email_sent_at_attribute_name, # when was email sent, used for hammering + # protection. + + :magic_login_mailer_class, # mailer class. Needed. + + :magic_login_mailer_disabled, # when true sorcery will not automatically + # email magic login details and allow you to + # manually handle how and when email is sent + + :magic_login_email_method_name, # magic login email method on your + # mailer class. + + :magic_login_expiration_period, # how many seconds before the request + # expires. nil for never expires. + + :magic_login_time_between_emails # hammering protection, how long to wait + # before allowing another email to be sent. + + end + + base.sorcery_config.instance_eval do + @defaults.merge!(:@magic_login_token_attribute_name => :magic_login_token, + :@magic_login_token_expires_at_attribute_name => :magic_login_token_expires_at, + :@magic_login_email_sent_at_attribute_name => :magic_login_email_sent_at, + :@magic_login_mailer_class => nil, + :@magic_login_mailer_disabled => true, + :@magic_login_email_method_name => :magic_login_email, + :@magic_login_expiration_period => 15 * 60, + :@magic_login_time_between_emails => 5 * 60) + + reset! + end + + base.extend(ClassMethods) + + base.sorcery_config.after_config << :validate_mailer_defined + base.sorcery_config.after_config << :define_magic_login_fields + + base.send(:include, InstanceMethods) + + end + + module ClassMethods + # Find user by token, also checks for expiration. + # Returns the user if token found and is valid. + def load_from_magic_login_token(token) + token_attr_name = @sorcery_config.magic_login_token_attribute_name + token_expiration_date_attr = @sorcery_config.magic_login_token_expires_at_attribute_name + load_from_token(token, token_attr_name, token_expiration_date_attr) + end + + protected + + # This submodule requires the developer to define his own mailer class to be used by it + # when magic_login_mailer_disabled is false + def validate_mailer_defined + msg = "To use magic_login submodule, you must define a mailer (config.magic_login_mailer_class = YourMailerClass)." + raise ArgumentError, msg if @sorcery_config.magic_login_mailer_class.nil? and @sorcery_config.magic_login_mailer_disabled == false + end + + def define_magic_login_fields + sorcery_adapter.define_field sorcery_config.magic_login_token_attribute_name, String + sorcery_adapter.define_field sorcery_config.magic_login_token_expires_at_attribute_name, Time + sorcery_adapter.define_field sorcery_config.magic_login_email_sent_at_attribute_name, Time + end + + end + + module InstanceMethods + # generates a reset code with expiration + def generate_magic_login_token! + config = sorcery_config + attributes = {config.magic_login_token_attribute_name => TemporaryToken.generate_random_token, + config.magic_login_email_sent_at_attribute_name => Time.now.in_time_zone} + attributes[config.magic_login_token_expires_at_attribute_name] = Time.now.in_time_zone + config.magic_login_expiration_period if config.magic_login_expiration_period + + self.sorcery_adapter.update_attributes(attributes) + end + + # generates a magic login code with expiration and sends an email to the user. + def deliver_magic_login_instructions! + mail = false + config = sorcery_config + # hammering protection + return false if !config.magic_login_time_between_emails.nil? && + self.send(config.magic_login_email_sent_at_attribute_name) && + self.send(config.magic_login_email_sent_at_attribute_name) > config.magic_login_time_between_emails.seconds.ago + + self.class.sorcery_adapter.transaction do + generate_magic_login_token! + unless config.magic_login_mailer_disabled + send_magic_login_email! + mail = true + end + end + mail + end + + # Clears the token. + def clear_magic_login_token! + config = sorcery_config + self.sorcery_adapter.update_attributes({ + config.magic_login_token_attribute_name => nil, + config.magic_login_token_expires_at_attribute_name => nil + }) + end + + protected + + def send_magic_login_email! + generic_send_email(:magic_login_email_method_name, :magic_login_mailer_class) + end + end + + end + end + end +end diff --git a/spec/active_record/user_magic_login_spec.rb b/spec/active_record/user_magic_login_spec.rb new file mode 100644 index 00000000..ea2e86aa --- /dev/null +++ b/spec/active_record/user_magic_login_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' +require 'shared_examples/user_magic_login_shared_examples' + +describe User, 'with magic_login submodule', active_record: true do + before(:all) do + ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/magic_login") + User.reset_column_information + end + + after(:all) do + ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/magic_login") + end + + it_behaves_like 'magic_login_model' +end diff --git a/spec/rails_app/app/mailers/sorcery_mailer.rb b/spec/rails_app/app/mailers/sorcery_mailer.rb index 4a415d43..bf68d59e 100644 --- a/spec/rails_app/app/mailers/sorcery_mailer.rb +++ b/spec/rails_app/app/mailers/sorcery_mailer.rb @@ -28,4 +28,11 @@ def send_unlock_token_email(user) mail(to: user.email, subject: 'Your account has been locked due to many wrong logins') end + + def magic_login_email(user) + @user = user + @url = 'http://example.com/login' + mail(to: user.email, + subject: 'Magic Login') + end end diff --git a/spec/rails_app/app/views/sorcery_mailer/magic_login_email.html.erb b/spec/rails_app/app/views/sorcery_mailer/magic_login_email.html.erb new file mode 100644 index 00000000..cf8243f3 --- /dev/null +++ b/spec/rails_app/app/views/sorcery_mailer/magic_login_email.html.erb @@ -0,0 +1,13 @@ + + + + + + +

Hello, <%= @user.username %>

+

+ To login without a password, just follow this link: <%= @url %>. +

+

Have a great day!

+ + diff --git a/spec/rails_app/app/views/sorcery_mailer/magic_login_email.text.erb b/spec/rails_app/app/views/sorcery_mailer/magic_login_email.text.erb new file mode 100644 index 00000000..64be0dd2 --- /dev/null +++ b/spec/rails_app/app/views/sorcery_mailer/magic_login_email.text.erb @@ -0,0 +1,6 @@ +Hello, <%= @user.username %> +=============================================== + +To login without a password, just follow this link: <%= @url %>. + +Have a great day! diff --git a/spec/rails_app/db/migrate/magic_login/20170924151831_add_magic_login_to_users.rb b/spec/rails_app/db/migrate/magic_login/20170924151831_add_magic_login_to_users.rb new file mode 100644 index 00000000..33196a6c --- /dev/null +++ b/spec/rails_app/db/migrate/magic_login/20170924151831_add_magic_login_to_users.rb @@ -0,0 +1,17 @@ +class AddMagicLoginToUsers < ActiveRecord::CompatibleLegacyMigration.migration_class + def self.up + add_column :users, :magic_login_token, :string, default: nil + add_column :users, :magic_login_token_expires_at, :datetime, default: nil + add_column :users, :magic_login_email_sent_at, :datetime, default: nil + + add_index :users, :magic_login_token + end + + def self.down + remove_index :users, :magic_login_token + + remove_column :users, :magic_login_token + remove_column :users, :magic_login_token_expires_at + remove_column :users, :magic_login_email_sent_at + end +end diff --git a/spec/shared_examples/user_magic_login_shared_examples.rb b/spec/shared_examples/user_magic_login_shared_examples.rb new file mode 100644 index 00000000..758a2526 --- /dev/null +++ b/spec/shared_examples/user_magic_login_shared_examples.rb @@ -0,0 +1,150 @@ +shared_examples_for "magic_login_model" do + let(:user) {create_new_user} + before(:each) do + User.sorcery_adapter.delete_all + end + + context 'loaded plugin configuration' do + let(:config) {User.sorcery_config} + + before(:all) do + sorcery_reload!([:magic_login]) + end + + after(:each) do + User.sorcery_config.reset! + end + + describe "enables configuration options" do + it do + sorcery_model_property_set(:magic_login_token_attribute_name, :test_magic_login_token) + expect(config.magic_login_token_attribute_name).to eq :test_magic_login_token + end + + it do + sorcery_model_property_set(:magic_login_token_expires_at_attribute_name, :test_magic_login_token_expires_at) + expect(config.magic_login_token_expires_at_attribute_name).to eq :test_magic_login_token_expires_at + end + + it do + sorcery_model_property_set(:magic_login_email_sent_at_attribute_name, :test_magic_login_email_sent_at) + expect(config.magic_login_email_sent_at_attribute_name).to eq :test_magic_login_email_sent_at + end + + it do + TestMailerClass = Class.new # need a mailer class to test + sorcery_model_property_set(:magic_login_mailer_class, TestMailerClass) + expect(config.magic_login_mailer_class).to eq TestMailerClass + end + + it do + sorcery_model_property_set(:magic_login_mailer_disabled, false) + expect(config.magic_login_mailer_disabled).to eq false + end + + it do + sorcery_model_property_set(:magic_login_email_method_name, :test_magic_login_email) + expect(config.magic_login_email_method_name).to eq :test_magic_login_email + end + + it do + sorcery_model_property_set(:magic_login_expiration_period, 100000000) + expect(config.magic_login_expiration_period).to eq 100000000 + end + + it do + sorcery_model_property_set(:magic_login_time_between_emails, 100000000) + expect(config.magic_login_time_between_emails).to eq 100000000 + end + end + + describe "#generate_magic_login_token!" do + context "magic_login_token is nil" do + it "magic_login_token_expires_at and magic_login_email_sent_at aren't nil " do + user.generate_magic_login_token! + expect(user.magic_login_token_expires_at).not_to be_nil + expect(user.magic_login_email_sent_at).not_to be_nil + end + + it "magic_login_token is different from the one before" do + token_before = user.magic_login_token + user.generate_magic_login_token! + expect(user.magic_login_token).not_to eq token_before + end + end + + context "magic_login_token is not nil" do + it "changes `user.magic_login_token`" do + token_before = user.magic_login_token + user.generate_magic_login_token! + expect(user.magic_login_token).not_to eq token_before + end + end + end + + describe "#deliver_magic_login_instructions!" do + context "success" do + before do + sorcery_model_property_set(:magic_login_time_between_emails, 30*60) + sorcery_model_property_set(:magic_login_mailer_disabled, false) + Timecop.travel(10.days.ago) do + user.send(:"#{config.magic_login_email_sent_at_attribute_name}=", DateTime.now) + end + sorcery_model_property_set(:magic_login_mailer_class, ::SorceryMailer) + end + + it do + user.deliver_magic_login_instructions! + expect(ActionMailer::Base.deliveries.size).to eq 1 + end + + it do + expect(user.deliver_magic_login_instructions!).to eq true + end + end + + context "failure" do + context "magic_login_time_between_emails is nil" do + it "returns false" do + sorcery_model_property_set(:magic_login_time_between_emails, nil) + expect(user.deliver_magic_login_instructions!).to eq false + end + end + + context "magic_login_email_sent_at is nil" do + it "returns false" do + user.send(:"#{config.magic_login_email_sent_at_attribute_name}=", nil) + expect(user.deliver_magic_login_instructions!).to eq false + end + end + + context "now is before magic_login_email_sent_at plus the interval" do + it "returns false" do + user.send(:"#{config.magic_login_email_sent_at_attribute_name}=", DateTime.now) + sorcery_model_property_set(:magic_login_time_between_emails, 30*60) + expect(user.deliver_magic_login_instructions!).to eq false + end + end + + context "magic_login_mailer_disabled is true" do + it "returns false" do + sorcery_model_property_set(:magic_login_mailer_disabled, true) + expect(user.deliver_magic_login_instructions!).to eq false + end + end + end + end + + describe "#clear_magic_login_token!" do + it "makes magic_login_token_attribute_name and magic_login_token_expires_at_attribute_name nil" do + user.magic_login_token = "test_token" + user.magic_login_token_expires_at = Time.now + + user.clear_magic_login_token! + + expect(user.magic_login_token).to eq nil + expect(user.magic_login_token_expires_at).to eq nil + end + end + end +end From f8a7c947e5df312bdb52dba1c46d89a54f9bfe93 Mon Sep 17 00:00:00 2001 From: Chase Gilliam Date: Tue, 28 Nov 2017 23:19:07 -0500 Subject: [PATCH 013/112] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9baa3367..e5640ddc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog ## HEAD + +* Add tests to the magic login submodule [#95](https://github.com/Sorcery/sorcery/pull/95) * Set user.stretches to 1 in test env by default [#81](https://github.com/Sorcery/sorcery/pull/81) * Allow user to be loaded from other source when session expires. fix #89 [#94](https://github.com/Sorcery/sorcery/pull/94) * Added a new ArgumentError for not defined user_class in config [#82](https://github.com/Sorcery/sorcery/pull/82) From be614baedbe87bf4735707f8e6676f71f414c70e Mon Sep 17 00:00:00 2001 From: Yusuke Ebihara Date: Wed, 13 Dec 2017 13:36:08 +0900 Subject: [PATCH 014/112] Make `#update_attributes` behavior identical to `#update` (#98) Another choice of implemention is like this: ``` @model.class.find_by(:"#{primary_key}" => @model.send(:"#{primary_key}")).update(attrs) ``` but for some reasons I don't adopt it. First it makes 2 queries instead of one. Secondly it has potential risk to race conditions. --- lib/sorcery/adapters/active_record_adapter.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/sorcery/adapters/active_record_adapter.rb b/lib/sorcery/adapters/active_record_adapter.rb index 266031e7..d5a956ef 100644 --- a/lib/sorcery/adapters/active_record_adapter.rb +++ b/lib/sorcery/adapters/active_record_adapter.rb @@ -6,7 +6,8 @@ def update_attributes(attrs) @model.send(:"#{name}=", value) end primary_key = @model.class.primary_key - @model.class.where(:"#{primary_key}" => @model.send(:"#{primary_key}")).update_all(attrs) + updated_count = @model.class.where(:"#{primary_key}" => @model.send(:"#{primary_key}")).update_all(attrs) + updated_count == 1 end def save(options = {}) From 986ebdde84d067e3f751d8f0b9ecd29aae49d7e9 Mon Sep 17 00:00:00 2001 From: Chase Gilliam Date: Tue, 12 Dec 2017 23:36:51 -0500 Subject: [PATCH 015/112] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5640ddc..51f74546 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## HEAD +* Make `#update_attributes` behave like `#update` [#98](https://github.com/Sorcery/sorcery/pull/98) * Add tests to the magic login submodule [#95](https://github.com/Sorcery/sorcery/pull/95) * Set user.stretches to 1 in test env by default [#81](https://github.com/Sorcery/sorcery/pull/81) * Allow user to be loaded from other source when session expires. fix #89 [#94](https://github.com/Sorcery/sorcery/pull/94) From a5fdfd23150a7a971ffe5a85ce1db3c69cd12aed Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Thu, 4 Jan 2018 20:27:31 -0800 Subject: [PATCH 016/112] Update yard dependency to minimum 0.9.12 (#100) --- CHANGELOG.md | 1 + sorcery.gemspec | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51f74546..e358ef10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * Add configuration for token randomness [#67](https://github.com/Sorcery/sorcery/pull/67) * Add facebook user_info_path option to initializer.rb [#63](https://github.com/Sorcery/sorcery/pull/63) * Add new function: `build_from` (allows building a user instance from OAuth without saving) [#54](https://github.com/Sorcery/sorcery/pull/54) +* Update yard minimum version to 0.9.12 ## 0.11.0 diff --git a/sorcery.gemspec b/sorcery.gemspec index 4bdfa7a8..ee3bbfb3 100644 --- a/sorcery.gemspec +++ b/sorcery.gemspec @@ -24,7 +24,7 @@ Gem::Specification.new do |s| s.add_dependency 'oauth2', '~> 1.0', '>= 0.8.0' s.add_dependency 'bcrypt', '~> 3.1' - s.add_development_dependency 'yard', '~> 0.6.0' + s.add_development_dependency 'yard', '~> 0.9.0', '>= 0.9.12' s.add_development_dependency 'timecop' s.add_development_dependency 'simplecov', '>= 0.3.8' s.add_development_dependency 'rspec-rails', '~> 3.5.0' From 44c5a68ca3987f551c700b2eea270a9c37a2a5c2 Mon Sep 17 00:00:00 2001 From: Chase Gilliam Date: Thu, 4 Jan 2018 23:27:59 -0500 Subject: [PATCH 017/112] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e358ef10..2e9e2982 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog ## HEAD - +* Update YARD dependency [#100](https://github.com/Sorcery/sorcery/pull/100) * Make `#update_attributes` behave like `#update` [#98](https://github.com/Sorcery/sorcery/pull/98) * Add tests to the magic login submodule [#95](https://github.com/Sorcery/sorcery/pull/95) * Set user.stretches to 1 in test env by default [#81](https://github.com/Sorcery/sorcery/pull/81) From 6aa57e76f722dda9a7ea69d89cd8794a2b7e93a4 Mon Sep 17 00:00:00 2001 From: FUNABARA Masao Date: Wed, 10 Jan 2018 04:32:47 +0900 Subject: [PATCH 018/112] Fix magic_login not inheriting from migration_class_name (#99) --- lib/generators/sorcery/templates/migration/magic_login.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/generators/sorcery/templates/migration/magic_login.rb b/lib/generators/sorcery/templates/migration/magic_login.rb index a1a4082f..6a1c9512 100644 --- a/lib/generators/sorcery/templates/migration/magic_login.rb +++ b/lib/generators/sorcery/templates/migration/magic_login.rb @@ -1,4 +1,4 @@ -class SorceryMagicLogin < ActiveRecord::Migration +class SorceryMagicLogin < <%= migration_class_name %> def change add_column :<%= model_class_name.tableize %>, :magic_login_token, :string, :default => nil add_column :<%= model_class_name.tableize %>, :magic_login_token_expires_at, :datetime, :default => nil From 30fe4fcf5a5309513ae439b725c8c47986eeaa82 Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Tue, 9 Jan 2018 11:35:59 -0800 Subject: [PATCH 019/112] Update CHANGELOG.md Added PR #99 and removed duplicate entry for PR #100 --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e9e2982..54e4c3b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog ## HEAD + +* Fix magic_login not inheriting from migration_class_name [#99](https://github.com/Sorcery/sorcery/pull/99) * Update YARD dependency [#100](https://github.com/Sorcery/sorcery/pull/100) * Make `#update_attributes` behave like `#update` [#98](https://github.com/Sorcery/sorcery/pull/98) * Add tests to the magic login submodule [#95](https://github.com/Sorcery/sorcery/pull/95) @@ -10,7 +12,6 @@ * Add configuration for token randomness [#67](https://github.com/Sorcery/sorcery/pull/67) * Add facebook user_info_path option to initializer.rb [#63](https://github.com/Sorcery/sorcery/pull/63) * Add new function: `build_from` (allows building a user instance from OAuth without saving) [#54](https://github.com/Sorcery/sorcery/pull/54) -* Update yard minimum version to 0.9.12 ## 0.11.0 From 996fa6105c1334dd6ffa8d5925904f250f8af24f Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Tue, 6 Feb 2018 18:08:34 -0800 Subject: [PATCH 020/112] Add rubocop config and todo (#107) * Add rubocop config and todo * Update changelog.md --- .rubocop.yml | 5 + .rubocop_todo.yml | 435 ++++++++++++++++++++++++++++++++++++++++++++++ CHANGELOG.md | 1 + 3 files changed, 441 insertions(+) create mode 100644 .rubocop.yml create mode 100644 .rubocop_todo.yml diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 00000000..67036565 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,5 @@ +# Use auto generated file to ignore existing warnings. +inherit_from: '.rubocop_todo.yml' + +AllCops: + TargetRubyVersion: 2.2 diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml new file mode 100644 index 00000000..c539bacd --- /dev/null +++ b/.rubocop_todo.yml @@ -0,0 +1,435 @@ +# This configuration was generated by +# `rubocop --auto-gen-config` +# on 2018-02-03 14:20:34 -0800 using RuboCop version 0.51.0. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: Include, TreatCommentsAsGroupSeparators. +# Include: **/Gemfile, **/gems.rb +Bundler/OrderedGems: + Exclude: + - 'Gemfile' + +# Offense count: 5 +# Cop supports --auto-correct. +# Configuration parameters: Include, TreatCommentsAsGroupSeparators. +# Include: **/*.gemspec +Gemspec/OrderedDependencies: + Exclude: + - 'sorcery.gemspec' + +# Offense count: 7 +# Cop supports --auto-correct. +Layout/EmptyLines: + Exclude: + - 'lib/generators/sorcery/templates/initializer.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: empty_lines, no_empty_lines +Layout/EmptyLinesAroundBlockBody: + Exclude: + - 'spec/rails_app/db/schema.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines +Layout/EmptyLinesAroundClassBody: + Exclude: + - 'lib/sorcery/adapters/mongoid_adapter.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +# Configuration parameters: AllowForAlignment, ForceEqualSignAlignment. +Layout/ExtraSpacing: + Exclude: + - 'lib/sorcery/adapters/mongoid_adapter.rb' + +# Offense count: 3 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. +# SupportedStyles: special_inside_parentheses, consistent, align_braces +Layout/IndentHash: + Exclude: + - 'lib/sorcery/model/submodules/magic_login.rb' + - 'lib/sorcery/providers/wechat.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. +# SupportedStyles: aligned, indented, indented_relative_to_receiver +Layout/MultilineMethodCallIndentation: + Exclude: + - 'lib/sorcery/adapters/mongoid_adapter.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. +# SupportedStyles: aligned, indented +Layout/MultilineOperationIndentation: + Exclude: + - 'lib/sorcery/model/submodules/magic_login.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +Layout/SpaceAfterComma: + Exclude: + - 'lib/sorcery/adapters/mongoid_adapter.rb' + - 'spec/controllers/controller_oauth2_spec.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: space, no_space +Layout/SpaceAroundEqualsInParameterDefault: + Exclude: + - 'lib/sorcery/adapters/mongoid_adapter.rb' + +# Offense count: 3 +# Cop supports --auto-correct. +# Configuration parameters: AllowForAlignment. +Layout/SpaceAroundOperators: + Exclude: + - 'lib/sorcery/adapters/mongoid_adapter.rb' + - 'spec/shared_examples/user_magic_login_shared_examples.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, SupportedStylesForEmptyBraces. +# SupportedStyles: space, no_space +# SupportedStylesForEmptyBraces: space, no_space +Layout/SpaceBeforeBlockBraces: + Exclude: + - 'lib/sorcery/providers/linkedin.rb' + +# Offense count: 8 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces, SpaceBeforeBlockParameters. +# SupportedStyles: space, no_space +# SupportedStylesForEmptyBraces: space, no_space +Layout/SpaceInsideBlockBraces: + Exclude: + - 'lib/sorcery/adapters/mongoid_adapter.rb' + - 'lib/sorcery/providers/linkedin.rb' + - 'spec/shared_examples/user_magic_login_shared_examples.rb' + +# Offense count: 4 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces. +# SupportedStyles: space, no_space, compact +# SupportedStylesForEmptyBraces: space, no_space +Layout/SpaceInsideHashLiteralBraces: + Exclude: + - 'lib/sorcery/adapters/mongoid_adapter.rb' + - 'lib/sorcery/model/submodules/magic_login.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: final_newline, final_blank_line +Layout/TrailingBlankLines: + Exclude: + - 'lib/sorcery/providers/wechat.rb' + +# Offense count: 52 +# Cop supports --auto-correct. +Layout/TrailingWhitespace: + Exclude: + - 'lib/sorcery/model/submodules/magic_login.rb' + - 'spec/active_record/user_magic_login_spec.rb' + - 'spec/rails_app/app/mailers/sorcery_mailer.rb' + - 'spec/rails_app/db/migrate/magic_login/20170924151831_add_magic_login_to_users.rb' + - 'spec/shared_examples/user_magic_login_shared_examples.rb' + +# Offense count: 1 +Lint/AmbiguousBlockAssociation: + Exclude: + - 'spec/shared_examples/user_shared_examples.rb' + +# Offense count: 28 +# Configuration parameters: AllowSafeAssignment. +Lint/AssignmentInCondition: + Exclude: + - 'lib/sorcery/controller/submodules/external.rb' + - 'lib/sorcery/providers/vk.rb' + - 'spec/rails_app/app/controllers/sorcery_controller.rb' + +# Offense count: 1 +Lint/DuplicateMethods: + Exclude: + - 'lib/sorcery/model/config.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyleAlignWith, SupportedStylesAlignWith, AutoCorrect. +# SupportedStylesAlignWith: keyword, variable, start_of_line +Lint/EndAlignment: + Exclude: + - 'lib/sorcery/model/config.rb' + +# Offense count: 5 +Lint/HandleExceptions: + Exclude: + - 'lib/sorcery/controller.rb' + - 'lib/sorcery/model.rb' + - 'spec/rails_app/config/application.rb' + - 'spec/shared_examples/user_shared_examples.rb' + +# Offense count: 1 +Lint/NonLocalExitFromIterator: + Exclude: + - 'lib/sorcery/controller.rb' + +# Offense count: 1 +Lint/ParenthesesAsGroupedExpression: + Exclude: + - 'spec/shared_examples/user_remember_me_shared_examples.rb' + +# Offense count: 3 +Lint/RescueWithoutErrorClass: + Exclude: + - 'lib/sorcery/controller/submodules/external.rb' + - 'spec/shared_examples/user_shared_examples.rb' + - 'spec/spec_helper.rb' + +# Offense count: 7 +Lint/UselessAssignment: + Exclude: + - 'lib/sorcery/controller/submodules/external.rb' + - 'lib/sorcery/model/submodules/external.rb' + - 'spec/controllers/controller_oauth2_spec.rb' + - 'spec/controllers/controller_remember_me_spec.rb' + +# Offense count: 2 +Lint/Void: + Exclude: + - 'spec/controllers/controller_remember_me_spec.rb' + +# Offense count: 28 +Metrics/AbcSize: + Max: 36 + +# Offense count: 73 +# Configuration parameters: CountComments, ExcludedMethods. +Metrics/BlockLength: + Max: 391 + +# Offense count: 1 +# Configuration parameters: CountComments. +Metrics/ClassLength: + Max: 303 + +# Offense count: 3 +Metrics/CyclomaticComplexity: + Max: 9 + +# Offense count: 833 +# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns. +# URISchemes: http, https +Metrics/LineLength: + Max: 323 + +# Offense count: 33 +# Configuration parameters: CountComments. +Metrics/MethodLength: + Max: 39 + +# Offense count: 1 +Metrics/PerceivedComplexity: + Max: 9 + +# Offense count: 11 +Naming/AccessorMethodName: + Exclude: + - 'lib/sorcery/adapters/mongoid_adapter.rb' + - 'lib/sorcery/controller/submodules/remember_me.rb' + - 'lib/sorcery/model/submodules/activity_logging.rb' + - 'lib/sorcery/protocols/oauth.rb' + - 'lib/sorcery/providers/jira.rb' + - 'lib/sorcery/providers/linkedin.rb' + - 'lib/sorcery/providers/twitter.rb' + - 'lib/sorcery/providers/xing.rb' + +# Offense count: 2 +# Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist, MethodDefinitionMacros. +# NamePrefix: is_, has_, have_ +# NamePrefixBlacklist: is_, has_, have_ +# NameWhitelist: is_a? +# MethodDefinitionMacros: define_method, define_singleton_method +Naming/PredicateName: + Exclude: + - 'spec/**/*' + - 'lib/sorcery/model/submodules/remember_me.rb' + - 'lib/sorcery/providers/base.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: always, conditionals +Style/AndOr: + Exclude: + - 'lib/sorcery/model/submodules/magic_login.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: braces, no_braces, context_dependent +Style/BracesAroundHashParameters: + Exclude: + - 'lib/sorcery/model/submodules/magic_login.rb' + +# Offense count: 1 +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: nested, compact +Style/ClassAndModuleChildren: + Exclude: + - 'lib/sorcery/test_helpers/internal.rb' + +# Offense count: 2 +Style/DateTime: + Exclude: + - 'spec/shared_examples/user_magic_login_shared_examples.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Style/Dir: + Exclude: + - 'lib/sorcery/controller/submodules/external.rb' + +# Offense count: 52 +Style/Documentation: + Enabled: false + +# Offense count: 2 +Style/DoubleNegation: + Exclude: + - 'lib/generators/sorcery/helpers.rb' + - 'lib/sorcery/controller.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: format, sprintf, percent +Style/FormatString: + Exclude: + - 'lib/generators/sorcery/install_generator.rb' + +# Offense count: 16 +# Configuration parameters: MinBodyLength. +Style/GuardClause: + Exclude: + - 'lib/generators/sorcery/install_generator.rb' + - 'lib/sorcery/controller.rb' + - 'lib/sorcery/controller/submodules/external.rb' + - 'lib/sorcery/model.rb' + - 'lib/sorcery/model/submodules/brute_force_protection.rb' + - 'lib/sorcery/test_helpers/internal.rb' + - 'lib/sorcery/test_helpers/internal/rails.rb' + - 'spec/rails_app/app/controllers/sorcery_controller.rb' + +# Offense count: 4 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols. +# SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys +Style/HashSyntax: + Exclude: + - 'lib/sorcery/adapters/mongoid_adapter.rb' + - 'spec/controllers/controller_oauth2_spec.rb' + - 'spec/rails_app/db/schema.rb' + +# Offense count: 1 +Style/MultipleComparison: + Exclude: + - 'lib/generators/sorcery/install_generator.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +Style/MutableConstant: + Exclude: + - 'lib/sorcery/test_helpers/internal/rails.rb' + - 'lib/sorcery/version.rb' + +# Offense count: 11 +# Cop supports --auto-correct. +# Configuration parameters: Strict. +Style/NumericLiterals: + MinDigits: 18 + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: AllowSafeAssignment. +Style/ParenthesesAroundCondition: + Exclude: + - 'lib/sorcery/adapters/mongoid_adapter.rb' + +# Offense count: 5 +# Cop supports --auto-correct. +# Configuration parameters: PreferredDelimiters. +Style/PercentLiteralDelimiters: + Exclude: + - 'lib/sorcery/test_helpers/internal/rails.rb' + - 'spec/controllers/controller_oauth2_spec.rb' + - 'spec/rails_app/config/application.rb' + - 'spec/sorcery_crypto_providers_spec.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Style/RedundantParentheses: + Exclude: + - 'lib/sorcery/adapters/mongoid_adapter.rb' + +# Offense count: 4 +# Cop supports --auto-correct. +Style/RedundantSelf: + Exclude: + - 'lib/sorcery/model/submodules/magic_login.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes. +# SupportedStyles: slashes, percent_r, mixed +Style/RegexpLiteral: + Exclude: + - 'spec/rails_app/config/application.rb' + +# Offense count: 89 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, ConsistentQuotesInMultiline. +# SupportedStyles: single_quotes, double_quotes +Style/StringLiterals: + Exclude: + - 'lib/sorcery/adapters/mongoid_adapter.rb' + - 'lib/sorcery/model/submodules/magic_login.rb' + - 'spec/controllers/controller_oauth2_spec.rb' + - 'spec/rails_app/db/schema.rb' + - 'spec/shared_examples/user_magic_login_shared_examples.rb' + +# Offense count: 22 +# Cop supports --auto-correct. +# Configuration parameters: MinSize, SupportedStyles. +# SupportedStyles: percent, brackets +Style/SymbolArray: + EnforcedStyle: brackets + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyleForMultiline, SupportedStylesForMultiline. +# SupportedStylesForMultiline: comma, consistent_comma, no_comma +Style/TrailingCommaInArguments: + Exclude: + - 'lib/sorcery/providers/wechat.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyleForMultiline, SupportedStylesForMultiline. +# SupportedStylesForMultiline: comma, consistent_comma, no_comma +Style/TrailingCommaInLiteral: + Exclude: + - 'lib/sorcery/providers/wechat.rb' + - 'spec/controllers/controller_oauth2_spec.rb' diff --git a/CHANGELOG.md b/CHANGELOG.md index 54e4c3b4..392f639b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ * Add configuration for token randomness [#67](https://github.com/Sorcery/sorcery/pull/67) * Add facebook user_info_path option to initializer.rb [#63](https://github.com/Sorcery/sorcery/pull/63) * Add new function: `build_from` (allows building a user instance from OAuth without saving) [#54](https://github.com/Sorcery/sorcery/pull/54) +* Add rubocop configuration and TODO list [#107](https://github.com/Sorcery/sorcery/pull/107) ## 0.11.0 From 5862b960a7bd811d2ebc31a9d195a62def9abbd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Fri, 23 Feb 2018 18:24:08 +0300 Subject: [PATCH 021/112] =?UTF-8?q?Add=20required=20version=20parameter=20?= =?UTF-8?q?when=20requesting=20user=20information=20from=20=E2=80=A6=20(#1?= =?UTF-8?q?09)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add required version parameter when requesting user information from vk provider [fix #108] Seems like now vk provider requires v (version) parameter to be present when calling their API: ``` curl -XGET https://api.vk.com/method/getProfiles\?user_id\=1 => {"error":{"error_code":8,"error_msg":"Invalid request: v (version) is required","request_params":[{"key":"oauth","value":"1"},{"key":"method","value":"getProfiles"},{"key":"user_id","value":"1"}]}} curl -XGET https://api.vk.com/method/getProfiles\?user_id\=1\&v\=5.71 {"response":[{"id":1,"first_name":"Павел","last_name":"Дуров"}]} ``` * Developer can specify vk API version in sorcery config [#108] * Fix indentation * Extract authorize with vk request stub into a method * Add VK api version to the sorcery initializer template [#108] * Update changelog.md --- CHANGELOG.md | 1 + .../sorcery/templates/initializer.rb | 1 + lib/sorcery/providers/vk.rb | 5 ++- sorcery.gemspec | 1 + spec/providers/vk_spec.rb | 41 +++++++++++++++++++ 5 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 spec/providers/vk_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 392f639b..11033d1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ * Add facebook user_info_path option to initializer.rb [#63](https://github.com/Sorcery/sorcery/pull/63) * Add new function: `build_from` (allows building a user instance from OAuth without saving) [#54](https://github.com/Sorcery/sorcery/pull/54) * Add rubocop configuration and TODO list [#107](https://github.com/Sorcery/sorcery/pull/107) +* Add support for VK OAuth (thanks to @Hirurg103) [#109](https://github.com/Sorcery/sorcery/pull/109) ## 0.11.0 diff --git a/lib/generators/sorcery/templates/initializer.rb b/lib/generators/sorcery/templates/initializer.rb index 36df69de..947902fe 100644 --- a/lib/generators/sorcery/templates/initializer.rb +++ b/lib/generators/sorcery/templates/initializer.rb @@ -154,6 +154,7 @@ # config.vk.secret = "" # config.vk.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=vk" # config.vk.user_info_mapping = {:login => "domain", :name => "full_name"} + # config.vk.api_version = "5.71" # # config.slack.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=slack" # config.slack.key = '' diff --git a/lib/sorcery/providers/vk.rb b/lib/sorcery/providers/vk.rb index 3e7fbcbc..82edf6f4 100644 --- a/lib/sorcery/providers/vk.rb +++ b/lib/sorcery/providers/vk.rb @@ -9,7 +9,7 @@ module Providers class Vk < Base include Protocols::Oauth2 - attr_accessor :auth_path, :token_path, :user_info_url, :scope + attr_accessor :auth_path, :token_path, :user_info_url, :scope, :api_version def initialize super @@ -28,7 +28,8 @@ def get_user_hash(access_token) access_token: access_token.token, uids: access_token.params['user_id'], fields: user_info_mapping.values.join(','), - scope: scope + scope: scope, + v: api_version.to_s } response = access_token.get(user_info_url, params: params) diff --git a/sorcery.gemspec b/sorcery.gemspec index ee3bbfb3..6a1a8dab 100644 --- a/sorcery.gemspec +++ b/sorcery.gemspec @@ -30,4 +30,5 @@ Gem::Specification.new do |s| s.add_development_dependency 'rspec-rails', '~> 3.5.0' s.add_development_dependency 'test-unit', '~> 3.1.0' s.add_development_dependency 'byebug', '~> 9.0.0' + s.add_development_dependency 'webmock', '~> 3.3.0' end diff --git a/spec/providers/vk_spec.rb b/spec/providers/vk_spec.rb new file mode 100644 index 00000000..0a885a16 --- /dev/null +++ b/spec/providers/vk_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' +require 'sorcery/providers/base' +require 'sorcery/providers/vk' +require 'webmock/rspec' + +describe Sorcery::Providers::Vk do + include WebMock::API + + let(:provider) { Sorcery::Controller::Config.vk } + + before(:all) do + sorcery_reload!([:external]) + sorcery_controller_property_set(:external_providers, [:vk]) + sorcery_controller_external_property_set(:vk, :key, "KEY") + sorcery_controller_external_property_set(:vk, :secret, "SECRET") + end + + def stub_vk_authorize + stub_request(:post, /https\:\/\/oauth\.vk\.com\/access_token/) + .to_return( + status: 200, + body: '{"access_token":"TOKEN","expires_in":86329,"user_id":1}', + headers: {'content-type' => 'application/json'}) + end + + context "getting user info hash" do + it "should provide VK API version" do + stub_vk_authorize + sorcery_controller_external_property_set(:vk, :api_version, '5.71') + + get_user = stub_request(:get, "https://api.vk.com/method/getProfiles?access_token=TOKEN&fields=&scope=email&uids=1&v=5.71") + .to_return(body: '{"response":[{"id":1}]}') + + token = provider.process_callback({ code: 'CODE' }, nil) + provider.get_user_hash(token) + + expect(get_user).to have_been_requested + end + end + +end From cf469b27ba76cbea310a557ce6b6c43eaee7611e Mon Sep 17 00:00:00 2001 From: Nicolas Leger Date: Mon, 12 Mar 2018 10:13:52 +0100 Subject: [PATCH 022/112] [CI] Test against Ruby 2.5 --- .travis.yml | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0087e2c3..3283ceaf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,10 @@ language: ruby rvm: - jruby - - 2.2.8 - - 2.3.5 - - 2.4.2 + - 2.2.9 + - 2.3.6 + - 2.4.3 + - 2.5.0 env: global: @@ -27,20 +28,21 @@ matrix: - rvm: jruby exclude: - - rvm: 2.2.8 + - rvm: 2.2.9 gemfile: gemfiles/active_record-rails40.gemfile - - - rvm: 2.3.5 + - rvm: 2.3.6 gemfile: gemfiles/active_record-rails40.gemfile - - - rvm: 2.4.2 + - rvm: 2.4.3 gemfile: gemfiles/active_record-rails40.gemfile - - - rvm: 2.4.2 + - rvm: 2.4.3 gemfile: gemfiles/active_record-rails41.gemfile - - - rvm: 2.4.2 + - rvm: 2.4.3 + gemfile: gemfiles/active_record-rails42.gemfile + - rvm: 2.5.0 + gemfile: gemfiles/active_record-rails40.gemfile + - rvm: 2.5.0 + gemfile: gemfiles/active_record-rails41.gemfile + - rvm: 2.5.0 gemfile: gemfiles/active_record-rails42.gemfile - - rvm: jruby gemfile: Gemfile From 82a31d54a73466543f1efbdc4cb5199a4f69ac4a Mon Sep 17 00:00:00 2001 From: Yusuke Ebihara Date: Fri, 13 Apr 2018 03:04:52 +0900 Subject: [PATCH 023/112] Fix issue #40 (#56) * Add access_count_to_reset_password_page to migration/reset_password.rb * Add #increment_password_reset_page_access_counter and #reset_password_reset_page_access_counter. * Add #increment_password_reset_page_access_counter and #reset_password_reset_page_access_counter * Add test suites related to #increment_password_reset_page_access_counter and #reset_password_reset_page_access_counter. * Refactor `#increment_password_reset_page_access_counter` Change manual increment into using Sorcery::Adapters::ActiveRecordAdapter.increment. --- .../sorcery/templates/initializer.rb | 5 +++++ .../templates/migration/reset_password.rb | 1 + lib/sorcery/model/submodules/reset_password.rb | 18 ++++++++++++++++++ ...101224223622_add_reset_password_to_users.rb | 2 ++ .../user_reset_password_shared_examples.rb | 16 ++++++++++++++++ 5 files changed, 42 insertions(+) diff --git a/lib/generators/sorcery/templates/initializer.rb b/lib/generators/sorcery/templates/initializer.rb index 947902fe..092d8eb2 100644 --- a/lib/generators/sorcery/templates/initializer.rb +++ b/lib/generators/sorcery/templates/initializer.rb @@ -366,6 +366,11 @@ # Default: `5 * 60` # # user.reset_password_time_between_emails = + + # access counter to a reset password page attribute name + # Default: `:access_count_to_reset_password_page` + # + # user.reset_password_page_access_count_attribute_name = # -- magic_login -- # magic login code attribute name. diff --git a/lib/generators/sorcery/templates/migration/reset_password.rb b/lib/generators/sorcery/templates/migration/reset_password.rb index cf00c0b8..21d32678 100644 --- a/lib/generators/sorcery/templates/migration/reset_password.rb +++ b/lib/generators/sorcery/templates/migration/reset_password.rb @@ -3,6 +3,7 @@ def change add_column :<%= model_class_name.tableize %>, :reset_password_token, :string, :default => nil add_column :<%= model_class_name.tableize %>, :reset_password_token_expires_at, :datetime, :default => nil add_column :<%= model_class_name.tableize %>, :reset_password_email_sent_at, :datetime, :default => nil + add_column :<%= model_class_name.tableize %>, :access_count_to_reset_password_page, :integer, :default => 0 add_index :<%= model_class_name.tableize %>, :reset_password_token end diff --git a/lib/sorcery/model/submodules/reset_password.rb b/lib/sorcery/model/submodules/reset_password.rb index 0948a63f..41d68bcc 100644 --- a/lib/sorcery/model/submodules/reset_password.rb +++ b/lib/sorcery/model/submodules/reset_password.rb @@ -16,6 +16,8 @@ def self.included(base) attr_accessor :reset_password_token_attribute_name # Expires at attribute name. attr_accessor :reset_password_token_expires_at_attribute_name + # Counter access to reset password page + attr_accessor :reset_password_page_access_count_attribute_name # When was email sent, used for hammering protection. attr_accessor :reset_password_email_sent_at_attribute_name # Mailer class (needed) @@ -34,6 +36,8 @@ def self.included(base) base.sorcery_config.instance_eval do @defaults.merge!(:@reset_password_token_attribute_name => :reset_password_token, :@reset_password_token_expires_at_attribute_name => :reset_password_token_expires_at, + :@reset_password_page_access_count_attribute_name => + :access_count_to_reset_password_page, :@reset_password_email_sent_at_attribute_name => :reset_password_email_sent_at, :@reset_password_mailer => nil, :@reset_password_mailer_disabled => false, @@ -103,6 +107,20 @@ def deliver_reset_password_instructions! end mail end + + # Increment access_count_to_reset_password_page attribute. + # For example, access_count_to_reset_password_page attribute is over 1, which + # means the user doesn't have a right to access. + def increment_password_reset_page_access_counter + sorcery_adapter.increment(self.sorcery_config.reset_password_page_access_count_attribute_name) + end + + # Reset access_count_to_reset_password_page attribute into 0. + # This is expected to be used after sending an instruction email. + def reset_password_reset_page_access_counter + send(:"#{sorcery_config.reset_password_page_access_count_attribute_name}=", 0) + sorcery_adapter.save + end # Clears token and tries to update the new password for the user. def change_password!(new_password) diff --git a/spec/rails_app/db/migrate/reset_password/20101224223622_add_reset_password_to_users.rb b/spec/rails_app/db/migrate/reset_password/20101224223622_add_reset_password_to_users.rb index e80e61f8..e4c6881d 100644 --- a/spec/rails_app/db/migrate/reset_password/20101224223622_add_reset_password_to_users.rb +++ b/spec/rails_app/db/migrate/reset_password/20101224223622_add_reset_password_to_users.rb @@ -3,11 +3,13 @@ def self.up add_column :users, :reset_password_token, :string, default: nil add_column :users, :reset_password_token_expires_at, :datetime, default: nil add_column :users, :reset_password_email_sent_at, :datetime, default: nil + add_column :users, :access_count_to_reset_password_page, :integer, default: 0 end def self.down remove_column :users, :reset_password_email_sent_at remove_column :users, :reset_password_token_expires_at remove_column :users, :reset_password_token + remove_column :users, :access_count_to_reset_password_page end end diff --git a/spec/shared_examples/user_reset_password_shared_examples.rb b/spec/shared_examples/user_reset_password_shared_examples.rb index 1f43e700..c5a7af8f 100644 --- a/spec/shared_examples/user_reset_password_shared_examples.rb +++ b/spec/shared_examples/user_reset_password_shared_examples.rb @@ -214,6 +214,22 @@ expect(user.reset_password_token).not_to eq old_password_code end + describe '#increment_password_reset_page_access_counter' do + it 'increments reset_password_page_access_count_attribute_name' do + expected_count = user.access_count_to_reset_password_page + 1 + user.increment_password_reset_page_access_counter + expect(user.access_count_to_reset_password_page).to eq expected_count + end + end + + describe '#reset_password_reset_page_access_counter' do + it 'reset reset_password_page_access_count_attribute_name into 0' do + user.update(access_count_to_reset_password_page: 10) + user.reset_password_reset_page_access_counter + expect(user.access_count_to_reset_password_page).to eq 0 + end + end + context 'mailer is enabled' do it 'sends an email on reset' do old_size = ActionMailer::Base.deliveries.size From d7d468abc6308a712f80162df54299d3c557bda9 Mon Sep 17 00:00:00 2001 From: Yusuke Ebihara Date: Fri, 13 Apr 2018 03:08:21 +0900 Subject: [PATCH 024/112] Add the request spec helper method, #login_user. (#57) * Add the request spec helper method, #login_user. * Fix codacy failure because of `Redundant `self` detected.` --- lib/sorcery.rb | 1 + lib/sorcery/test_helpers/rails/request.rb | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 lib/sorcery/test_helpers/rails/request.rb diff --git a/lib/sorcery.rb b/lib/sorcery.rb index d70266cf..2a0af8cc 100644 --- a/lib/sorcery.rb +++ b/lib/sorcery.rb @@ -57,6 +57,7 @@ module TestHelpers module Rails require 'sorcery/test_helpers/rails/controller' require 'sorcery/test_helpers/rails/integration' + require 'sorcery/test_helpers/rails/request' end module Internal diff --git a/lib/sorcery/test_helpers/rails/request.rb b/lib/sorcery/test_helpers/rails/request.rb new file mode 100644 index 00000000..83f3bd1a --- /dev/null +++ b/lib/sorcery/test_helpers/rails/request.rb @@ -0,0 +1,20 @@ +module Sorcery + module TestHelpers + module Rails + module Request + # Accepts arguments for user to login, the password, route to use and HTTP method + # Defaults - @user, 'secret', 'user_sessions_url' and http_method: POST + def login_user(user = nil, password = 'secret', route = nil, http_method = :post) + user ||= @user + route ||= user_sessions_url + + username_attr = user.sorcery_config.username_attribute_names.first + username = user.send(username_attr) + password_attr = user.sorcery_config.password_attribute_name + + send(http_method, route, params: { "#{username_attr}": username, "#{password_attr}": password }) + end + end + end + end +end From ddb73b5e36a582528733b7ca0bf8b80f0814e9ca Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Thu, 12 Apr 2018 11:13:51 -0700 Subject: [PATCH 025/112] Update CHANGELOG.md Add CHANGELOG entries for #56 and #57 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11033d1e..4da42d23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ * Add new function: `build_from` (allows building a user instance from OAuth without saving) [#54](https://github.com/Sorcery/sorcery/pull/54) * Add rubocop configuration and TODO list [#107](https://github.com/Sorcery/sorcery/pull/107) * Add support for VK OAuth (thanks to @Hirurg103) [#109](https://github.com/Sorcery/sorcery/pull/109) +* Fix token leak via referrer header [#56](https://github.com/Sorcery/sorcery/pull/56) +* Add `login_user` helper for request specs [#57](https://github.com/Sorcery/sorcery/pull/57) ## 0.11.0 From 7b24a9250b8570748d1b8d4b775558215773731d Mon Sep 17 00:00:00 2001 From: Joshua Buker Date: Thu, 12 Apr 2018 13:25:11 -0700 Subject: [PATCH 026/112] Update Gem dependencies Rails 5.2 appears to have deprecations that break our test suite, and solving the issue would take more time than I have available currently. Updating to 5.1 for now. --- Gemfile | 2 +- sorcery.gemspec | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 7d2e1a5b..ae6bb55e 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source 'https://rubygems.org' -gem 'rails', '~> 5.0.0' +gem 'rails', '~> 5.1.0' gem 'rails-controller-testing' gem 'sqlite3' gem 'pry' diff --git a/sorcery.gemspec b/sorcery.gemspec index 6a1a8dab..4668a2a3 100644 --- a/sorcery.gemspec +++ b/sorcery.gemspec @@ -27,8 +27,8 @@ Gem::Specification.new do |s| s.add_development_dependency 'yard', '~> 0.9.0', '>= 0.9.12' s.add_development_dependency 'timecop' s.add_development_dependency 'simplecov', '>= 0.3.8' - s.add_development_dependency 'rspec-rails', '~> 3.5.0' - s.add_development_dependency 'test-unit', '~> 3.1.0' - s.add_development_dependency 'byebug', '~> 9.0.0' + s.add_development_dependency 'rspec-rails', '~> 3.7.0' + s.add_development_dependency 'test-unit', '~> 3.2.0' + s.add_development_dependency 'byebug', '~> 10.0.0' s.add_development_dependency 'webmock', '~> 3.3.0' end From ba45f1823948702bdbae3f24b2f20e2a6ee239ab Mon Sep 17 00:00:00 2001 From: Joshua Buker Date: Mon, 23 Apr 2018 15:55:18 -0700 Subject: [PATCH 027/112] Release 0.12.0 --- CHANGELOG.md | 2 ++ lib/sorcery/version.rb | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4da42d23..9baa7a5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## HEAD +## 0.12.0 + * Fix magic_login not inheriting from migration_class_name [#99](https://github.com/Sorcery/sorcery/pull/99) * Update YARD dependency [#100](https://github.com/Sorcery/sorcery/pull/100) * Make `#update_attributes` behave like `#update` [#98](https://github.com/Sorcery/sorcery/pull/98) diff --git a/lib/sorcery/version.rb b/lib/sorcery/version.rb index 7d2e2123..1c8791d0 100644 --- a/lib/sorcery/version.rb +++ b/lib/sorcery/version.rb @@ -1,3 +1,3 @@ module Sorcery - VERSION = '0.11.0' + VERSION = '0.12.0' end From 8f3cb2d9ec1daaa30ed4c1cde01226a85b5b88f5 Mon Sep 17 00:00:00 2001 From: Akhil Naini Date: Wed, 20 Jun 2018 12:38:32 -0700 Subject: [PATCH 028/112] Rails 5.2 support (#129) * Rails 5.2 support * Fix specs for Rails 4 --- Gemfile | 2 +- lib/generators/sorcery/install_generator.rb | 2 +- spec/active_record/user_activation_spec.rb | 4 +- .../user_activity_logging_spec.rb | 4 +- .../user_brute_force_protection_spec.rb | 4 +- spec/active_record/user_magic_login_spec.rb | 4 +- spec/active_record/user_oauth_spec.rb | 4 +- spec/active_record/user_remember_me_spec.rb | 4 +- .../active_record/user_reset_password_spec.rb | 4 +- spec/active_record/user_spec.rb | 10 ---- .../controller_http_basic_auth_spec.rb | 2 +- spec/controllers/controller_oauth2_spec.rb | 26 +++------- spec/controllers/controller_oauth_spec.rb | 4 +- .../controller_session_timeout_spec.rb | 6 +-- spec/controllers/controller_spec.rb | 2 +- spec/orm/active_record.rb | 4 +- spec/rails_app/config/application.rb | 4 +- .../config/initializers/secret_token.rb | 7 --- spec/rails_app/config/secrets.yml | 4 ++ spec/shared_examples/user_shared_examples.rb | 49 ++++++++----------- spec/support/migration_helper.rb | 19 +++++++ 21 files changed, 77 insertions(+), 92 deletions(-) delete mode 100644 spec/rails_app/config/initializers/secret_token.rb create mode 100644 spec/rails_app/config/secrets.yml create mode 100644 spec/support/migration_helper.rb diff --git a/Gemfile b/Gemfile index ae6bb55e..c7c8f10c 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source 'https://rubygems.org' -gem 'rails', '~> 5.1.0' +gem 'rails', '~> 5.2.0' gem 'rails-controller-testing' gem 'sqlite3' gem 'pry' diff --git a/lib/generators/sorcery/install_generator.rb b/lib/generators/sorcery/install_generator.rb index 8c9db77b..b03171bb 100644 --- a/lib/generators/sorcery/install_generator.rb +++ b/lib/generators/sorcery/install_generator.rb @@ -61,7 +61,7 @@ def inject_sorcery_to_model # Copy the migrations files to db/migrate folder def copy_migration_files # Copy core migration file in all cases except when you pass --only-submodules. - return unless defined?(Sorcery::Generators::InstallGenerator::ActiveRecord) + return unless defined?(ActiveRecord) migration_template 'migration/core.rb', 'db/migrate/sorcery_core.rb', migration_class_name: migration_class_name unless only_submodules? if submodules diff --git a/spec/active_record/user_activation_spec.rb b/spec/active_record/user_activation_spec.rb index 82a7020d..4d9f62ac 100644 --- a/spec/active_record/user_activation_spec.rb +++ b/spec/active_record/user_activation_spec.rb @@ -5,12 +5,12 @@ describe User, 'with activation submodule', active_record: true do before(:all) do - ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/activation") + MigrationHelper.migrate("#{Rails.root}/db/migrate/activation") User.reset_column_information end after(:all) do - ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/activation") + MigrationHelper.rollback("#{Rails.root}/db/migrate/activation") end it_behaves_like 'rails_3_activation_model' diff --git a/spec/active_record/user_activity_logging_spec.rb b/spec/active_record/user_activity_logging_spec.rb index b185b45b..3b3c90d9 100644 --- a/spec/active_record/user_activity_logging_spec.rb +++ b/spec/active_record/user_activity_logging_spec.rb @@ -3,12 +3,12 @@ describe User, 'with activity logging submodule', active_record: true do before(:all) do - ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/activity_logging") + MigrationHelper.migrate("#{Rails.root}/db/migrate/activity_logging") User.reset_column_information end after(:all) do - ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/activity_logging") + MigrationHelper.rollback("#{Rails.root}/db/migrate/activity_logging") end it_behaves_like 'rails_3_activity_logging_model' diff --git a/spec/active_record/user_brute_force_protection_spec.rb b/spec/active_record/user_brute_force_protection_spec.rb index 544b8ff6..6bc25811 100644 --- a/spec/active_record/user_brute_force_protection_spec.rb +++ b/spec/active_record/user_brute_force_protection_spec.rb @@ -3,12 +3,12 @@ describe User, 'with brute_force_protection submodule', active_record: true do before(:all) do - ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/brute_force_protection") + MigrationHelper.migrate("#{Rails.root}/db/migrate/brute_force_protection") User.reset_column_information end after(:all) do - ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/brute_force_protection") + MigrationHelper.rollback("#{Rails.root}/db/migrate/brute_force_protection") end it_behaves_like 'rails_3_brute_force_protection_model' diff --git a/spec/active_record/user_magic_login_spec.rb b/spec/active_record/user_magic_login_spec.rb index ea2e86aa..a561e739 100644 --- a/spec/active_record/user_magic_login_spec.rb +++ b/spec/active_record/user_magic_login_spec.rb @@ -3,12 +3,12 @@ describe User, 'with magic_login submodule', active_record: true do before(:all) do - ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/magic_login") + MigrationHelper.migrate("#{Rails.root}/db/migrate/magic_login") User.reset_column_information end after(:all) do - ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/magic_login") + MigrationHelper.rollback("#{Rails.root}/db/migrate/magic_login") end it_behaves_like 'magic_login_model' diff --git a/spec/active_record/user_oauth_spec.rb b/spec/active_record/user_oauth_spec.rb index 33a751be..0f5b779f 100644 --- a/spec/active_record/user_oauth_spec.rb +++ b/spec/active_record/user_oauth_spec.rb @@ -3,12 +3,12 @@ describe User, 'with oauth submodule', active_record: true do before(:all) do - ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/external") + MigrationHelper.migrate("#{Rails.root}/db/migrate/external") User.reset_column_information end after(:all) do - ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/external") + MigrationHelper.rollback("#{Rails.root}/db/migrate/external") end it_behaves_like 'rails_3_oauth_model' diff --git a/spec/active_record/user_remember_me_spec.rb b/spec/active_record/user_remember_me_spec.rb index 948b3c08..0c5ef16c 100644 --- a/spec/active_record/user_remember_me_spec.rb +++ b/spec/active_record/user_remember_me_spec.rb @@ -3,12 +3,12 @@ describe User, 'with remember_me submodule', active_record: true do before(:all) do - ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/remember_me") + MigrationHelper.migrate("#{Rails.root}/db/migrate/remember_me") User.reset_column_information end after(:all) do - ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/remember_me") + MigrationHelper.rollback("#{Rails.root}/db/migrate/remember_me") end it_behaves_like 'rails_3_remember_me_model' diff --git a/spec/active_record/user_reset_password_spec.rb b/spec/active_record/user_reset_password_spec.rb index 1af1f266..3cac7186 100644 --- a/spec/active_record/user_reset_password_spec.rb +++ b/spec/active_record/user_reset_password_spec.rb @@ -3,12 +3,12 @@ describe User, 'with reset_password submodule', active_record: true do before(:all) do - ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/reset_password") + MigrationHelper.migrate("#{Rails.root}/db/migrate/reset_password") User.reset_column_information end after(:all) do - ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/reset_password") + MigrationHelper.rollback("#{Rails.root}/db/migrate/reset_password") end it_behaves_like 'rails_3_reset_password_model' diff --git a/spec/active_record/user_spec.rb b/spec/active_record/user_spec.rb index 407eeef1..9c982821 100644 --- a/spec/active_record/user_spec.rb +++ b/spec/active_record/user_spec.rb @@ -22,16 +22,6 @@ it_should_behave_like 'rails_3_core_model' describe 'external users' do - before(:all) do - ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/external") - User.reset_column_information - sorcery_reload! - end - - after(:all) do - ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/external") - end - it_should_behave_like 'external_user' end end diff --git a/spec/controllers/controller_http_basic_auth_spec.rb b/spec/controllers/controller_http_basic_auth_spec.rb index 7a633a2c..f6b8f94f 100644 --- a/spec/controllers/controller_http_basic_auth_spec.rb +++ b/spec/controllers/controller_http_basic_auth_spec.rb @@ -28,7 +28,7 @@ expect(User).to receive('authenticate').with('bla@bla.com', 'secret').and_return(user) get :test_http_basic_auth, params: {}, session: { http_authentication_used: true } - expect(response).to be_a_success + expect(response).to be_successful end it 'fails authentication if credentials are wrong' do diff --git a/spec/controllers/controller_oauth2_spec.rb b/spec/controllers/controller_oauth2_spec.rb index 653564d8..4ac6bb75 100644 --- a/spec/controllers/controller_oauth2_spec.rb +++ b/spec/controllers/controller_oauth2_spec.rb @@ -5,7 +5,9 @@ describe SorceryController, active_record: true, type: :controller do before(:all) do if SORCERY_ORM == :active_record - ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/external") + MigrationHelper.migrate("#{Rails.root}/db/migrate/external") + MigrationHelper.migrate("#{Rails.root}/db/migrate/activation") + MigrationHelper.migrate("#{Rails.root}/db/migrate/activity_logging") User.reset_column_information end @@ -15,7 +17,9 @@ after(:all) do if SORCERY_ORM == :active_record - ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/external") + MigrationHelper.rollback("#{Rails.root}/db/migrate/external") + MigrationHelper.rollback("#{Rails.root}/db/migrate/activity_logging") + MigrationHelper.rollback("#{Rails.root}/db/migrate/activation") end end @@ -196,10 +200,6 @@ describe 'OAuth with User Activation features' do before(:all) do - if SORCERY_ORM == :active_record - ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/activation") - end - sorcery_reload!([:user_activation,:external], :user_activation_mailer => ::SorceryMailer) sorcery_controller_property_set(:external_providers, [:facebook, :github, :google, :liveid, :vk, :salesforce, :paypal, :slack, :wechat, :microsoft]) @@ -236,13 +236,6 @@ sorcery_controller_external_property_set(:microsoft, :callback_url, "http://blabla.com") end - after(:all) do - if SORCERY_ORM == :active_record - ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/external") - ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/activation") - end - end - after(:each) do User.sorcery_adapter.delete_all end @@ -286,13 +279,6 @@ sorcery_reload!([:activity_logging, :external]) end - after(:all) do - if SORCERY_ORM == :active_record - ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/external") - ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/activity_logging") - end - end - %w(facebook github google liveid vk salesforce slack).each do |provider| context "when #{provider}" do before(:each) do diff --git a/spec/controllers/controller_oauth_spec.rb b/spec/controllers/controller_oauth_spec.rb index c796f084..13d934d2 100644 --- a/spec/controllers/controller_oauth_spec.rb +++ b/spec/controllers/controller_oauth_spec.rb @@ -215,7 +215,7 @@ def stub_all_oauth_requests! describe SorceryController, 'OAuth with session timeout features' do before(:all) do if SORCERY_ORM == :active_record - ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/external") + MigrationHelper.migrate("#{Rails.root}/db/migrate/external") User.reset_column_information end @@ -224,7 +224,7 @@ def stub_all_oauth_requests! after(:all) do if SORCERY_ORM == :active_record - ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/external") + MigrationHelper.rollback("#{Rails.root}/db/migrate/external") end end diff --git a/spec/controllers/controller_session_timeout_spec.rb b/spec/controllers/controller_session_timeout_spec.rb index 3abc2eda..17029e93 100644 --- a/spec/controllers/controller_session_timeout_spec.rb +++ b/spec/controllers/controller_session_timeout_spec.rb @@ -24,7 +24,7 @@ get :test_should_be_logged_in expect(session[:user_id]).not_to be_nil - expect(response).to be_a_success + expect(response).to be_successful end it 'resets session after session timeout' do @@ -44,7 +44,7 @@ get :test_login, params: { email: 'bla@bla.com', password: 'secret' } expect(session[:user_id]).not_to be_nil - expect(response).to be_a_success + expect(response).to be_successful end context "with 'session_timeout_from_last_action'" do @@ -62,7 +62,7 @@ get :test_should_be_logged_in expect(session[:user_id]).not_to be_nil - expect(response).to be_a_success + expect(response).to be_successful end it "with 'session_timeout_from_last_action' logs out if there was no activity" do diff --git a/spec/controllers/controller_spec.rb b/spec/controllers/controller_spec.rb index a1ba1744..9d564e9c 100644 --- a/spec/controllers/controller_spec.rb +++ b/spec/controllers/controller_spec.rb @@ -132,7 +132,7 @@ sorcery_controller_property_set(:not_authenticated_action, :test_not_authenticated_action) get :test_logout - expect(response).to be_a_success + expect(response).to be_successful end it 'require_login before_action saves the url that the user originally wanted' do diff --git a/spec/orm/active_record.rb b/spec/orm/active_record.rb index 50a1c874..9aec5857 100644 --- a/spec/orm/active_record.rb +++ b/spec/orm/active_record.rb @@ -9,11 +9,11 @@ class TestUser < ActiveRecord::Base end def setup_orm - ActiveRecord::Migrator.migrate(migrations_path) + MigrationHelper.migrate(migrations_path) end def teardown_orm - ActiveRecord::Migrator.rollback(migrations_path) + MigrationHelper.rollback(migrations_path) end def migrations_path diff --git a/spec/rails_app/config/application.rb b/spec/rails_app/config/application.rb index 44b2a9ca..9d4f3dbd 100644 --- a/spec/rails_app/config/application.rb +++ b/spec/rails_app/config/application.rb @@ -50,7 +50,9 @@ class Application < Rails::Application config.filter_parameters += [:password] config.action_mailer.delivery_method = :test - config.active_support.deprecation = :stderr + if Rails.version >= '5.1.0' && config.active_record.sqlite3.present? + config.active_record.sqlite3.represent_boolean_as_integer = true + end end end diff --git a/spec/rails_app/config/initializers/secret_token.rb b/spec/rails_app/config/initializers/secret_token.rb deleted file mode 100644 index 2971ba22..00000000 --- a/spec/rails_app/config/initializers/secret_token.rb +++ /dev/null @@ -1,7 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Your secret key for verifying the integrity of signed cookies. -# If you change this key, all old signed cookies will become invalid! -# Make sure the secret is at least 30 characters and all random, -# no regular words or you'll be exposed to dictionary attacks. -AppRoot::Application.config.secret_token = 'a9789f869a0d0ac2f2b683d6e9410c530696b178bca28a7971f4a652b14ff2da89f2cf4dcbf0355f6bc41f81731aa8e46085674d1acc1980436f61cdba76ff5d' diff --git a/spec/rails_app/config/secrets.yml b/spec/rails_app/config/secrets.yml new file mode 100644 index 00000000..fdcc61e2 --- /dev/null +++ b/spec/rails_app/config/secrets.yml @@ -0,0 +1,4 @@ +# secrets.yml + +test: + secret_key_base: 'a9789f869a0d0ac2f2b683d6e9410c530696b178bca28a7971f4a652b14ff2da89f2cf4dcbf0355f6bc41f81731aa8e46085674d1acc1980436f61cdba76ff5d' diff --git a/spec/shared_examples/user_shared_examples.rb b/spec/shared_examples/user_shared_examples.rb index c563cc95..e5d8e735 100644 --- a/spec/shared_examples/user_shared_examples.rb +++ b/spec/shared_examples/user_shared_examples.rb @@ -308,12 +308,12 @@ class Admin2 < User; end describe 'generic send email' do before(:all) do - ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/activation") + MigrationHelper.migrate("#{Rails.root}/db/migrate/activation") User.reset_column_information end after(:all) do - ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/activation") + MigrationHelper.rollback("#{Rails.root}/db/migrate/activation") end before do @@ -518,6 +518,21 @@ def self.matches?(crypted, *tokens) let(:user) { create_new_user } let(:external_user) { create_new_external_user :twitter } + before(:all) do + if SORCERY_ORM == :active_record + MigrationHelper.migrate("#{Rails.root}/db/migrate/external") + MigrationHelper.migrate("#{Rails.root}/db/migrate/activation") + end + sorcery_reload! + end + + after(:all) do + if SORCERY_ORM == :active_record + MigrationHelper.rollback("#{Rails.root}/db/migrate/external") + MigrationHelper.rollback("#{Rails.root}/db/migrate/activation") + end + end + before(:each) do User.sorcery_adapter.delete_all end @@ -535,24 +550,12 @@ def self.matches?(crypted, *tokens) end describe '.create_from_provider' do - before(:all) do - if SORCERY_ORM == :active_record - ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/external") - User.reset_column_information - end - + before(:each) do sorcery_reload!([:external]) - end - - after(:all) do - if SORCERY_ORM == :active_record - ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/external") - end + sorcery_model_property_set(:authentications_class, Authentication) end it 'supports nested attributes' do - sorcery_model_property_set(:authentications_class, Authentication) - expect do User.create_from_provider('facebook', '123', username: 'Noam Ben Ari') end.to change { User.count }.by(1) @@ -576,22 +579,10 @@ def self.matches?(crypted, *tokens) end describe 'activation' do - before(:all) do - if SORCERY_ORM == :active_record - ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/external") - ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/activation") - end - + before(:each) do sorcery_reload!([:user_activation, :external], user_activation_mailer: ::SorceryMailer) end - after(:all) do - if SORCERY_ORM == :active_record - ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/external") - ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/activation") - end - end - after(:each) do User.sorcery_adapter.delete_all end diff --git a/spec/support/migration_helper.rb b/spec/support/migration_helper.rb new file mode 100644 index 00000000..27c8f299 --- /dev/null +++ b/spec/support/migration_helper.rb @@ -0,0 +1,19 @@ +class MigrationHelper + class << self + def migrate(path) + if ActiveRecord.version >= Gem::Version.new('5.2.0') + ActiveRecord::MigrationContext.new(path).migrate + else + ActiveRecord::Migrator.migrate(path) + end + end + + def rollback(path) + if ActiveRecord.version >= Gem::Version.new('5.2.0') + ActiveRecord::MigrationContext.new(path).rollback + else + ActiveRecord::Migrator.rollback(path) + end + end + end +end From cc186295b936508313887d9bcc2d3fcecc3c12ea Mon Sep 17 00:00:00 2001 From: hkdnet Date: Thu, 21 Jun 2018 04:44:52 +0900 Subject: [PATCH 029/112] Guard clause for top-level constant, ActiveRecord (#128) Sorcery::Generators::InstallGenerator::ActiveRecord is not defined and the purpose of this guard is to know whether orm is ActiveRecord or not. Therefore, return early when top-level ActiveRecord is not defined. From 2fea1d78361976286c79795d0740b89f8e4156b6 Mon Sep 17 00:00:00 2001 From: Andrew Hood Date: Mon, 9 Jul 2018 11:22:02 -0700 Subject: [PATCH 030/112] Support injecting helpers into ActionController::API (#133) Rails API apps use the lighter alternative to ActionController::Base. This adds support for injecting these helpers into either action controller base class that the consuming application happens to inherit from. --- lib/sorcery/engine.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/sorcery/engine.rb b/lib/sorcery/engine.rb index 8042cf9a..77be7648 100644 --- a/lib/sorcery/engine.rb +++ b/lib/sorcery/engine.rb @@ -8,9 +8,11 @@ class Engine < Rails::Engine config.sorcery = ::Sorcery::Controller::Config initializer 'extend Controller with sorcery' do - ActionController::Base.send(:include, Sorcery::Controller) - ActionController::Base.helper_method :current_user - ActionController::Base.helper_method :logged_in? + ActiveSupport.on_load('action_controller') do + send(:include, Sorcery::Controller) + helper_method :current_user + helper_method :logged_in? + end end end end From 8959f8c736866133271fdfe29c071b6e04229506 Mon Sep 17 00:00:00 2001 From: Alexey Naumov Date: Mon, 9 Jul 2018 21:23:10 +0300 Subject: [PATCH 031/112] send activation email after the commit in transactional databases (#130) * send activation email after the commit in transactional databases * extract callback_name to separate method * fix typo in parameter * disable transactional fixtures to be able to test after_commit * Raise on failure when change_password! --- lib/sorcery/adapters/active_record_adapter.rb | 2 +- lib/sorcery/adapters/mongoid_adapter.rb | 10 +++++++++- lib/sorcery/model/submodules/reset_password.rb | 6 +++--- lib/sorcery/model/submodules/user_activation.rb | 2 +- spec/spec_helper.rb | 2 +- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/lib/sorcery/adapters/active_record_adapter.rb b/lib/sorcery/adapters/active_record_adapter.rb index d5a956ef..c778fa48 100644 --- a/lib/sorcery/adapters/active_record_adapter.rb +++ b/lib/sorcery/adapters/active_record_adapter.rb @@ -35,7 +35,7 @@ def define_field(name, type, options = {}) end def define_callback(time, event, method_name, options = {}) - @klass.send "#{time}_#{event}", method_name, options.slice(:if) + @klass.send "#{time}_#{event}", method_name, options.slice(:if, :on) end def find_by_oauth_credentials(provider, uid) diff --git a/lib/sorcery/adapters/mongoid_adapter.rb b/lib/sorcery/adapters/mongoid_adapter.rb index 9009c335..baace2da 100644 --- a/lib/sorcery/adapters/mongoid_adapter.rb +++ b/lib/sorcery/adapters/mongoid_adapter.rb @@ -33,7 +33,15 @@ def define_field(name, type, options={}) end def define_callback(time, event, method_name, options={}) - @klass.send "#{time}_#{event}", method_name, options.slice(:if) + @klass.send callback_name(time, event, options), method_name, options.slice(:if) + end + + def callback_name(time, event, options) + if event == :commit + options[:on] == :create ? "#{time}_create" : "#{time}_save" + else + "#{time}_#{event}" + end end def credential_regex(credential) diff --git a/lib/sorcery/model/submodules/reset_password.rb b/lib/sorcery/model/submodules/reset_password.rb index 41d68bcc..1c0a5f7f 100644 --- a/lib/sorcery/model/submodules/reset_password.rb +++ b/lib/sorcery/model/submodules/reset_password.rb @@ -107,14 +107,14 @@ def deliver_reset_password_instructions! end mail end - + # Increment access_count_to_reset_password_page attribute. # For example, access_count_to_reset_password_page attribute is over 1, which # means the user doesn't have a right to access. def increment_password_reset_page_access_counter sorcery_adapter.increment(self.sorcery_config.reset_password_page_access_count_attribute_name) end - + # Reset access_count_to_reset_password_page attribute into 0. # This is expected to be used after sending an instruction email. def reset_password_reset_page_access_counter @@ -126,7 +126,7 @@ def reset_password_reset_page_access_counter def change_password!(new_password) clear_reset_password_token send(:"#{sorcery_config.password_attribute_name}=", new_password) - sorcery_adapter.save + sorcery_adapter.save raise_on_failure: true end protected diff --git a/lib/sorcery/model/submodules/user_activation.rb b/lib/sorcery/model/submodules/user_activation.rb index 1c58bbbb..9d65e1d6 100644 --- a/lib/sorcery/model/submodules/user_activation.rb +++ b/lib/sorcery/model/submodules/user_activation.rb @@ -45,7 +45,7 @@ def self.included(base) # don't setup activation if no password supplied - this user is created automatically sorcery_adapter.define_callback :before, :create, :setup_activation, if: proc { |user| user.send(sorcery_config.password_attribute_name).present? } # don't send activation needed email if no crypted password created - this user is external (OAuth etc.) - sorcery_adapter.define_callback :after, :create, :send_activation_needed_email!, if: :send_activation_needed_email? + sorcery_adapter.define_callback :after, :commit, :send_activation_needed_email!, on: :create, if: :send_activation_needed_email? end base.sorcery_config.after_config << :validate_mailer_defined diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index c8d9782f..6c033504 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -29,7 +29,7 @@ class TestMailer < ActionMailer::Base; end config.include RSpec::Rails::ControllerExampleGroup, file_path: /controller(.)*_spec.rb$/ config.mock_with :rspec - config.use_transactional_fixtures = true + config.use_transactional_fixtures = false config.before(:suite) { setup_orm } config.after(:suite) { teardown_orm } From cc7c46f68ee945ad72c32e37fb1e9b86ebc9579f Mon Sep 17 00:00:00 2001 From: Jordan King Date: Mon, 9 Jul 2018 13:24:59 -0500 Subject: [PATCH 032/112] Add an opt in invalidate_active_sessions! method to session timeout (#110) * Add an opt in invalidate_activate_sessions! method to session timeout Spike on adding a configurable `invalid_active_sessions! method to the session_timeouts module. Configurable, but expects an `invalidate_sessions_before` timestamp column on user. Does not invalidate the session if `invalidate_sessions_before` is not set meaning that if it is deployed it won't log out currently logged in users. * Add a test that you can login after calling `invalidate_active_sessions!` There was concern that because `invalidate_sessions_before` is never cleared, there may be an issue where sessions would be cleared immediately after login. Add a spec to verify that sessions logged in after the `invalidate_sessions_before` timestamp can login and make additional requests without being cleared. * Update the RREADME to document `invalidate_acitve_sessions!` method * Move `invalidate_active_sessions!` method to no longer be protected This method never should have been private as it was meant to be part of the public API if enabled * Make the time check to invalidate the session more defensive This would throw an error if session login_time and last_action_time were not set, be more defensive and default to current time if those values aren't set. --- README.md | 7 ++ .../sorcery/templates/initializer.rb | 5 ++ .../controller/submodules/session_timeout.rb | 24 +++++- .../controller_session_timeout_spec.rb | 81 +++++++++++++++++++ .../app/controllers/sorcery_controller.rb | 5 ++ spec/rails_app/config/routes.rb | 1 + ...alidate_active_sessions_before_to_users.rb | 9 +++ 7 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 spec/rails_app/db/migrate/invalidate_active_sessions/20180221093235_add_invalidate_active_sessions_before_to_users.rb diff --git a/README.md b/README.md index 7f927359..73da614d 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,12 @@ User.load_from_reset_password_token(token) @user.change_password!(new_password) ``` +### Session Timeout + +```ruby +invalidate_active_sessions! #Invalidate all sessions with a login_time or last_action_time before the current time. Must Opt-in +``` + ### User Activation ```ruby @@ -184,6 +190,7 @@ Inside the initializer, the comments will tell you what each setting does. - Configurable session timeout - Optionally session timeout will be calculated from last user action +- Optionally enable a method to clear all active sessions, expects an `invalidate_sessions_before` datetime attribute. **Brute Force Protection** (see [lib/sorcery/model/submodules/brute_force_protection.rb](https://github.com/Sorcery/sorcery/blob/master/lib/sorcery/model/submodules/brute_force_protection.rb)): diff --git a/lib/generators/sorcery/templates/initializer.rb b/lib/generators/sorcery/templates/initializer.rb index 092d8eb2..65f3fcbe 100644 --- a/lib/generators/sorcery/templates/initializer.rb +++ b/lib/generators/sorcery/templates/initializer.rb @@ -46,6 +46,11 @@ # # config.session_timeout_from_last_action = + # Invalidate active sessions Requires an `invalidate_sessions_before` timestamp column + # Default: `false` + # + # config.session_timeout_invalidate_active_sessions_enabled = + # -- http_basic_auth -- # What realm to display for which controller name. For example {"My App" => "Application"} # Default: `{"application" => "Application"}` diff --git a/lib/sorcery/controller/submodules/session_timeout.rb b/lib/sorcery/controller/submodules/session_timeout.rb index e5b6924d..119d26cb 100644 --- a/lib/sorcery/controller/submodules/session_timeout.rb +++ b/lib/sorcery/controller/submodules/session_timeout.rb @@ -12,10 +12,13 @@ class << self attr_accessor :session_timeout # use the last action as the beginning of session timeout. attr_accessor :session_timeout_from_last_action + # allow users to invalidate active sessions + attr_accessor :session_timeout_invalidate_active_sessions_enabled def merge_session_timeout_defaults! - @defaults.merge!(:@session_timeout => 3600, # 1.hour - :@session_timeout_from_last_action => false) + @defaults.merge!(:@session_timeout => 3600, # 1.hour + :@session_timeout_from_last_action => false, + :@session_timeout_invalidate_active_sessions_enabled => false) end end merge_session_timeout_defaults! @@ -25,6 +28,13 @@ def merge_session_timeout_defaults! end module InstanceMethods + def invalidate_active_sessions! + return unless Config.session_timeout_invalidate_active_sessions_enabled + return unless current_user.present? + current_user.send(:invalidate_sessions_before=, Time.now.in_time_zone) + current_user.save + end + protected # Registers last login to be used as the timeout starting point. @@ -37,7 +47,7 @@ def register_login_time(_user, _credentials) # To be used as a before_action, before require_login def validate_session session_to_use = Config.session_timeout_from_last_action ? session[:last_action_time] : session[:login_time] - if session_to_use && sorcery_session_expired?(session_to_use.to_time) + if (session_to_use && sorcery_session_expired?(session_to_use.to_time)) || sorcery_session_invalidated? reset_sorcery_session remove_instance_variable :@current_user if defined? @current_user else @@ -48,6 +58,14 @@ def validate_session def sorcery_session_expired?(time) Time.now.in_time_zone - time > Config.session_timeout end + + # Use login time if present, otherwise use last action time. + def sorcery_session_invalidated? + return false unless Config.session_timeout_invalidate_active_sessions_enabled + return false unless current_user.present? && current_user.try(:invalidate_sessions_before).present? + time = session[:login_time] || session[:last_action_time] || Time.now.in_time_zone + time < current_user.invalidate_sessions_before + end end end end diff --git a/spec/controllers/controller_session_timeout_spec.rb b/spec/controllers/controller_session_timeout_spec.rb index 17029e93..7f06ae66 100644 --- a/spec/controllers/controller_session_timeout_spec.rb +++ b/spec/controllers/controller_session_timeout_spec.rb @@ -36,6 +36,87 @@ expect(response).to be_a_redirect end + context "with 'invalidate_active_sessions_enabled'" do + it 'does not reset the session if invalidate_sessions_before is nil' do + sorcery_controller_property_set(:session_timeout_invalidate_active_sessions_enabled, true) + login_user user + allow(user).to receive(:invalidate_sessions_before) { nil } + + get :test_should_be_logged_in + + expect(session[:user_id]).not_to be_nil + expect(response).to be_a_success + end + + it 'does not reset the session if it was not created before invalidate_sessions_before' do + sorcery_controller_property_set(:session_timeout_invalidate_active_sessions_enabled, true) + login_user user + allow(user).to receive(:invalidate_sessions_before) { Time.now.in_time_zone - 10.minutes } + + get :test_should_be_logged_in + + expect(session[:user_id]).not_to be_nil + expect(response).to be_a_success + end + + it 'resets the session if the session was created before invalidate_sessions_before' do + sorcery_controller_property_set(:session_timeout_invalidate_active_sessions_enabled, true) + login_user user + allow(user).to receive(:invalidate_sessions_before) { Time.now.in_time_zone } + get :test_should_be_logged_in + + expect(session[:user_id]).to be_nil + expect(response).to be_a_redirect + end + + it 'resets active sessions on next action if invalidate_active_sessions! is called' do + sorcery_controller_property_set(:session_timeout_invalidate_active_sessions_enabled, true) + # precondition that the user is logged in + login_user user + get :test_should_be_logged_in + expect(response).to be_a_success + + allow(user).to receive(:send) { |_method, value| allow(user).to receive(:invalidate_sessions_before) { value } } + allow(user).to receive(:save) + get :test_invalidate_active_session + expect(response).to be_a_success + + get :test_should_be_logged_in + expect(session[:user_id]).to be_nil + expect(response).to be_a_redirect + end + + it 'allows login after invalidate_active_sessions! is called' do + sorcery_controller_property_set(:session_timeout_invalidate_active_sessions_enabled, true) + # precondition that the user is logged in + login_user user + get :test_should_be_logged_in + expect(response).to be_a_success + + allow(user).to receive(:send) { |_method, value| allow(user).to receive(:invalidate_sessions_before) { value } } + allow(user).to receive(:save) + # Call to invalidate + get :test_invalidate_active_session + expect(response).to be_a_success + + # Check that existing sessions were logged out + get :test_should_be_logged_in + expect(session[:user_id]).to be_nil + expect(response).to be_a_redirect + + # Check that new session is allowed to login + login_user user + get :test_should_be_logged_in + expect(response).to be_a_success + expect(session[:user_id]).not_to be_nil + + # Check an additional request to make sure not logged out on next request + get :test_should_be_logged_in + expect(response).to be_a_success + expect(session[:user_id]).not_to be_nil + end + end + it 'works if the session is stored as a string or a Time' do session[:login_time] = Time.now.to_s # TODO: ??? diff --git a/spec/rails_app/app/controllers/sorcery_controller.rb b/spec/rails_app/app/controllers/sorcery_controller.rb index 1d916fe5..0d77fae0 100644 --- a/spec/rails_app/app/controllers/sorcery_controller.rb +++ b/spec/rails_app/app/controllers/sorcery_controller.rb @@ -52,6 +52,11 @@ def test_logout_with_force_forget_me head :ok end + def test_invalidate_active_session + invalidate_active_sessions! + head :ok + end + def test_login_with_remember @user = login(params[:email], params[:password]) remember_me! diff --git a/spec/rails_app/config/routes.rb b/spec/rails_app/config/routes.rb index 712d7f6a..2f63fa99 100644 --- a/spec/rails_app/config/routes.rb +++ b/spec/rails_app/config/routes.rb @@ -12,6 +12,7 @@ get :test_login_from get :test_logout_with_remember get :test_logout_with_force_forget_me + get :test_invalidate_active_session get :test_should_be_logged_in get :test_create_from_provider get :test_add_second_provider diff --git a/spec/rails_app/db/migrate/invalidate_active_sessions/20180221093235_add_invalidate_active_sessions_before_to_users.rb b/spec/rails_app/db/migrate/invalidate_active_sessions/20180221093235_add_invalidate_active_sessions_before_to_users.rb new file mode 100644 index 00000000..23eef7f3 --- /dev/null +++ b/spec/rails_app/db/migrate/invalidate_active_sessions/20180221093235_add_invalidate_active_sessions_before_to_users.rb @@ -0,0 +1,9 @@ +class AddInvalidateSessionsBeforeToUsers < ActiveRecord::CompatibleLegacyMigration.migration_class + def self.up + add_column :users, :invalidate_sessions_before, :datetime, default: nil + end + + def self.down + remove_column :users, :invalidate_sessions_before + end +end From ff46dc5fe1e87fe36fd652326364f082e6b4c7af Mon Sep 17 00:00:00 2001 From: Peter Postma Date: Mon, 9 Jul 2018 20:26:52 +0200 Subject: [PATCH 033/112] Namespace class method #encrypt. (#77) This fixes a conflict with attr_encrypted, making both gems work in the same model. --- lib/sorcery/model.rb | 4 ++-- spec/shared_examples/user_shared_examples.rb | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/sorcery/model.rb b/lib/sorcery/model.rb index c36fb2fd..1bdd424c 100644 --- a/lib/sorcery/model.rb +++ b/lib/sorcery/model.rb @@ -119,7 +119,7 @@ def authenticate(*credentials, &block) end # encrypt tokens using current encryption_provider. - def encrypt(*tokens) + def sorcery_encrypt(*tokens) return tokens.first if @sorcery_config.encryption_provider.nil? set_encryption_attributes @@ -185,7 +185,7 @@ def valid_password?(pass) def encrypt_password config = sorcery_config send(:"#{config.salt_attribute_name}=", new_salt = TemporaryToken.generate_random_token) unless config.salt_attribute_name.nil? - send(:"#{config.crypted_password_attribute_name}=", self.class.encrypt(send(config.password_attribute_name), new_salt)) + send(:"#{config.crypted_password_attribute_name}=", self.class.sorcery_encrypt(send(config.password_attribute_name), new_salt)) end def clear_virtual_password diff --git a/spec/shared_examples/user_shared_examples.rb b/spec/shared_examples/user_shared_examples.rb index e5d8e735..0cc79668 100644 --- a/spec/shared_examples/user_shared_examples.rb +++ b/spec/shared_examples/user_shared_examples.rb @@ -173,7 +173,7 @@ end end - specify { expect(User).to respond_to(:encrypt) } + specify { expect(User).to respond_to(:sorcery_encrypt) } it 'subclass inherits config if defined so' do sorcery_reload!([], subclasses_inherit_config: true) @@ -412,11 +412,11 @@ def self.matches?(crypted, *tokens) sorcery_model_property_set(:encryption_algorithm, :aes256) sorcery_model_property_set(:encryption_key, nil) - expect { User.encrypt @text }.to raise_error(ArgumentError) + expect { User.sorcery_encrypt @text }.to raise_error(ArgumentError) sorcery_model_property_set(:encryption_key, 'asd234dfs423fddsmndsflktsdf32343') - expect { User.encrypt @text }.not_to raise_error + expect { User.sorcery_encrypt @text }.not_to raise_error end it 'if encryption algo is aes256, it sets key to crypto provider, even if attributes are set in reverse' do @@ -425,31 +425,31 @@ def self.matches?(crypted, *tokens) sorcery_model_property_set(:encryption_key, 'asd234dfs423fddsmndsflktsdf32343') sorcery_model_property_set(:encryption_algorithm, :aes256) - expect { User.encrypt @text }.not_to raise_error + expect { User.sorcery_encrypt @text }.not_to raise_error end it 'if encryption algo is md5 it works' do sorcery_model_property_set(:encryption_algorithm, :md5) - expect(User.encrypt(@text)).to eq Sorcery::CryptoProviders::MD5.encrypt(@text) + expect(User.sorcery_encrypt(@text)).to eq Sorcery::CryptoProviders::MD5.encrypt(@text) end it 'if encryption algo is sha1 it works' do sorcery_model_property_set(:encryption_algorithm, :sha1) - expect(User.encrypt(@text)).to eq Sorcery::CryptoProviders::SHA1.encrypt(@text) + expect(User.sorcery_encrypt(@text)).to eq Sorcery::CryptoProviders::SHA1.encrypt(@text) end it 'if encryption algo is sha256 it works' do sorcery_model_property_set(:encryption_algorithm, :sha256) - expect(User.encrypt(@text)).to eq Sorcery::CryptoProviders::SHA256.encrypt(@text) + expect(User.sorcery_encrypt(@text)).to eq Sorcery::CryptoProviders::SHA256.encrypt(@text) end it 'if encryption algo is sha512 it works' do sorcery_model_property_set(:encryption_algorithm, :sha512) - expect(User.encrypt(@text)).to eq Sorcery::CryptoProviders::SHA512.encrypt(@text) + expect(User.sorcery_encrypt(@text)).to eq Sorcery::CryptoProviders::SHA512.encrypt(@text) end it 'salt is random for each user and saved in db' do From fcd71407f8990b45d594f20e54b420b6c3404bec Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Mon, 9 Jul 2018 14:11:38 -0700 Subject: [PATCH 034/112] Revert "Namespace class method #encrypt. (#77)" (#135) This reverts commit ff46dc5fe1e87fe36fd652326364f082e6b4c7af. --- lib/sorcery/model.rb | 4 ++-- spec/shared_examples/user_shared_examples.rb | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/sorcery/model.rb b/lib/sorcery/model.rb index 1bdd424c..c36fb2fd 100644 --- a/lib/sorcery/model.rb +++ b/lib/sorcery/model.rb @@ -119,7 +119,7 @@ def authenticate(*credentials, &block) end # encrypt tokens using current encryption_provider. - def sorcery_encrypt(*tokens) + def encrypt(*tokens) return tokens.first if @sorcery_config.encryption_provider.nil? set_encryption_attributes @@ -185,7 +185,7 @@ def valid_password?(pass) def encrypt_password config = sorcery_config send(:"#{config.salt_attribute_name}=", new_salt = TemporaryToken.generate_random_token) unless config.salt_attribute_name.nil? - send(:"#{config.crypted_password_attribute_name}=", self.class.sorcery_encrypt(send(config.password_attribute_name), new_salt)) + send(:"#{config.crypted_password_attribute_name}=", self.class.encrypt(send(config.password_attribute_name), new_salt)) end def clear_virtual_password diff --git a/spec/shared_examples/user_shared_examples.rb b/spec/shared_examples/user_shared_examples.rb index 0cc79668..e5d8e735 100644 --- a/spec/shared_examples/user_shared_examples.rb +++ b/spec/shared_examples/user_shared_examples.rb @@ -173,7 +173,7 @@ end end - specify { expect(User).to respond_to(:sorcery_encrypt) } + specify { expect(User).to respond_to(:encrypt) } it 'subclass inherits config if defined so' do sorcery_reload!([], subclasses_inherit_config: true) @@ -412,11 +412,11 @@ def self.matches?(crypted, *tokens) sorcery_model_property_set(:encryption_algorithm, :aes256) sorcery_model_property_set(:encryption_key, nil) - expect { User.sorcery_encrypt @text }.to raise_error(ArgumentError) + expect { User.encrypt @text }.to raise_error(ArgumentError) sorcery_model_property_set(:encryption_key, 'asd234dfs423fddsmndsflktsdf32343') - expect { User.sorcery_encrypt @text }.not_to raise_error + expect { User.encrypt @text }.not_to raise_error end it 'if encryption algo is aes256, it sets key to crypto provider, even if attributes are set in reverse' do @@ -425,31 +425,31 @@ def self.matches?(crypted, *tokens) sorcery_model_property_set(:encryption_key, 'asd234dfs423fddsmndsflktsdf32343') sorcery_model_property_set(:encryption_algorithm, :aes256) - expect { User.sorcery_encrypt @text }.not_to raise_error + expect { User.encrypt @text }.not_to raise_error end it 'if encryption algo is md5 it works' do sorcery_model_property_set(:encryption_algorithm, :md5) - expect(User.sorcery_encrypt(@text)).to eq Sorcery::CryptoProviders::MD5.encrypt(@text) + expect(User.encrypt(@text)).to eq Sorcery::CryptoProviders::MD5.encrypt(@text) end it 'if encryption algo is sha1 it works' do sorcery_model_property_set(:encryption_algorithm, :sha1) - expect(User.sorcery_encrypt(@text)).to eq Sorcery::CryptoProviders::SHA1.encrypt(@text) + expect(User.encrypt(@text)).to eq Sorcery::CryptoProviders::SHA1.encrypt(@text) end it 'if encryption algo is sha256 it works' do sorcery_model_property_set(:encryption_algorithm, :sha256) - expect(User.sorcery_encrypt(@text)).to eq Sorcery::CryptoProviders::SHA256.encrypt(@text) + expect(User.encrypt(@text)).to eq Sorcery::CryptoProviders::SHA256.encrypt(@text) end it 'if encryption algo is sha512 it works' do sorcery_model_property_set(:encryption_algorithm, :sha512) - expect(User.sorcery_encrypt(@text)).to eq Sorcery::CryptoProviders::SHA512.encrypt(@text) + expect(User.encrypt(@text)).to eq Sorcery::CryptoProviders::SHA512.encrypt(@text) end it 'salt is random for each user and saved in db' do From 1a3c39908b46c880fe52cc0a4bb66441d61b1f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lign=C3=A9?= Date: Wed, 18 Jul 2018 16:06:10 +0200 Subject: [PATCH 035/112] Pass along `remember_me` to `#auto_login` (#136) When overriding the `#auto_login` method when implementing our own session handling we never got passed the `should_remember` value from the `#login` method. The `Sorcery::Controller::Submodules::RememberMe` never used the value of the `should_remember` arguments passed to the `#auto_login` method and instead relied on a callback being executed. This commits removes the callback and instead calls the `#remember_me!` method from the `#auto_login` method instead. --- lib/sorcery/controller.rb | 2 +- lib/sorcery/controller/submodules/remember_me.rb | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/sorcery/controller.rb b/lib/sorcery/controller.rb index 50738851..cfbf1cdc 100644 --- a/lib/sorcery/controller.rb +++ b/lib/sorcery/controller.rb @@ -47,7 +47,7 @@ def login(*credentials) end form_authenticity_token - auto_login(user) + auto_login(user, credentials[2]) after_login!(user, credentials) block_given? ? yield(current_user, nil) : current_user diff --git a/lib/sorcery/controller/submodules/remember_me.rb b/lib/sorcery/controller/submodules/remember_me.rb index 48ccce9e..055c45d5 100644 --- a/lib/sorcery/controller/submodules/remember_me.rb +++ b/lib/sorcery/controller/submodules/remember_me.rb @@ -18,7 +18,6 @@ def merge_remember_me_defaults! merge_remember_me_defaults! end Config.login_sources << :login_from_cookie - Config.after_login << :remember_me_if_asked_to Config.before_logout << :forget_me! end @@ -51,12 +50,6 @@ def auto_login(user, should_remember = false) protected - # calls remember_me! if a third credential was passed to the login method. - # Runs as a hook after login. - def remember_me_if_asked_to(_user, credentials) - remember_me! if credentials.size == 3 && credentials[2] && credentials[2] != '0' - end - # Checks the cookie for a remember me token, tried to find a user with that token # and logs the user in if found. # Runs as a login source. See 'current_user' method for how it is used. From eea7a1c852a9da8a6729cedacd7c4c292a2897d7 Mon Sep 17 00:00:00 2001 From: Titus Ramczykowski Date: Wed, 1 Aug 2018 17:54:22 +0200 Subject: [PATCH 036/112] Register login time for session on login from cookie (#102) Before, an unlimited session was created on login from cookie (remember me) even if the SessionTimeout submodule was active. Logging in via the remember me function will now correctly set the session timeout. --- lib/sorcery/controller.rb | 4 ++++ lib/sorcery/controller/config.rb | 2 ++ lib/sorcery/controller/submodules/remember_me.rb | 1 + lib/sorcery/controller/submodules/session_timeout.rb | 3 ++- spec/controllers/controller_remember_me_spec.rb | 2 ++ spec/controllers/controller_session_timeout_spec.rb | 6 ++++++ 6 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/sorcery/controller.rb b/lib/sorcery/controller.rb index cfbf1cdc..3344ba19 100644 --- a/lib/sorcery/controller.rb +++ b/lib/sorcery/controller.rb @@ -153,6 +153,10 @@ def after_logout!(user) Config.after_logout.each { |c| send(c, user) } end + def after_remember_me!(user) + Config.after_remember_me.each { |c| send(c, user) } + end + def user_class @user_class ||= Config.user_class.to_s.constantize rescue NameError diff --git a/lib/sorcery/controller/config.rb b/lib/sorcery/controller/config.rb index 99e1f3fb..40e247f9 100644 --- a/lib/sorcery/controller/config.rb +++ b/lib/sorcery/controller/config.rb @@ -18,6 +18,7 @@ class << self attr_accessor :after_failed_login attr_accessor :before_logout attr_accessor :after_logout + attr_accessor :after_remember_me def init! @defaults = { @@ -29,6 +30,7 @@ def init! :@after_failed_login => [], :@before_logout => [], :@after_logout => [], + :@after_remember_me => [], :@save_return_to_url => true, :@cookie_domain => nil } diff --git a/lib/sorcery/controller/submodules/remember_me.rb b/lib/sorcery/controller/submodules/remember_me.rb index 055c45d5..1bcad7e5 100644 --- a/lib/sorcery/controller/submodules/remember_me.rb +++ b/lib/sorcery/controller/submodules/remember_me.rb @@ -58,6 +58,7 @@ def login_from_cookie if user && user.has_remember_me_token? set_remember_me_cookie!(user) session[:user_id] = user.id.to_s + after_remember_me!(user) @current_user = user else @current_user = false diff --git a/lib/sorcery/controller/submodules/session_timeout.rb b/lib/sorcery/controller/submodules/session_timeout.rb index 119d26cb..08dfd285 100644 --- a/lib/sorcery/controller/submodules/session_timeout.rb +++ b/lib/sorcery/controller/submodules/session_timeout.rb @@ -24,6 +24,7 @@ def merge_session_timeout_defaults! merge_session_timeout_defaults! end Config.after_login << :register_login_time + Config.after_remember_me << :register_login_time base.prepend_before_action :validate_session end @@ -39,7 +40,7 @@ def invalidate_active_sessions! # Registers last login to be used as the timeout starting point. # Runs as a hook after a successful login. - def register_login_time(_user, _credentials) + def register_login_time(_user, _credentials = nil) session[:login_time] = session[:last_action_time] = Time.now.in_time_zone end diff --git a/spec/controllers/controller_remember_me_spec.rb b/spec/controllers/controller_remember_me_spec.rb index b9908764..11c331cb 100644 --- a/spec/controllers/controller_remember_me_spec.rb +++ b/spec/controllers/controller_remember_me_spec.rb @@ -80,6 +80,8 @@ expect(User.sorcery_adapter).to receive(:find_by_remember_me_token).with('token').and_return(user) + expect(subject).to receive(:after_remember_me!).with(user) + get :test_login_from_cookie expect(assigns[:current_user]).to eq user diff --git a/spec/controllers/controller_session_timeout_spec.rb b/spec/controllers/controller_session_timeout_spec.rb index 7f06ae66..2a3495ce 100644 --- a/spec/controllers/controller_session_timeout_spec.rb +++ b/spec/controllers/controller_session_timeout_spec.rb @@ -156,5 +156,11 @@ expect(response).to be_a_redirect end end + + it "registers login time on remember_me callback" do + expect(subject).to receive(:register_login_time).with(user) + + subject.send(:after_remember_me!, user) + end end end From 12ccd6485e487529cec807a0775c88b55821c080 Mon Sep 17 00:00:00 2001 From: Hugo Gilmar Erazo Date: Thu, 20 Sep 2018 12:32:12 -0600 Subject: [PATCH 037/112] demodulize added on authentication class name association name fetch (#147) * demodulize added on authentication class name association name fetch * changelog entry added for PR #147 * Add missing `#` to changelog entry --- CHANGELOG.md | 1 + lib/sorcery/model/submodules/external.rb | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9baa7a5c..fa207d4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ * Add support for VK OAuth (thanks to @Hirurg103) [#109](https://github.com/Sorcery/sorcery/pull/109) * Fix token leak via referrer header [#56](https://github.com/Sorcery/sorcery/pull/56) * Add `login_user` helper for request specs [#57](https://github.com/Sorcery/sorcery/pull/57) +* Added `demodulize` on authentication class name association name fetch [#147](https://github.com/Sorcery/sorcery/pull/147) ## 0.11.0 diff --git a/lib/sorcery/model/submodules/external.rb b/lib/sorcery/model/submodules/external.rb index 3a84c23b..864bc3ef 100644 --- a/lib/sorcery/model/submodules/external.rb +++ b/lib/sorcery/model/submodules/external.rb @@ -45,7 +45,7 @@ def load_from_provider(provider, uid) def create_and_validate_from_provider(provider, uid, attrs) user = new(attrs) - user.send(sorcery_config.authentications_class.to_s.downcase.pluralize).build( + user.send(sorcery_config.authentications_class.name.demodulize.underscore.pluralize).build( sorcery_config.provider_uid_attribute_name => uid, sorcery_config.provider_attribute_name => provider ) @@ -92,7 +92,7 @@ def build_from_provider(attrs) module InstanceMethods def add_provider_to_user(provider, uid) - authentications = sorcery_config.authentications_class.name.underscore.pluralize + authentications = sorcery_config.authentications_class.name.demodulize.underscore.pluralize # first check to see if user has a particular authentication already if sorcery_adapter.find_authentication_by_oauth_credentials(authentications, provider, uid).nil? user = send(authentications).build(sorcery_config.provider_uid_attribute_name => uid, From f4f08f83ac7129908b2f5908ca84bb824ab997c1 Mon Sep 17 00:00:00 2001 From: Igor Zubkov Date: Thu, 20 Sep 2018 21:58:20 +0300 Subject: [PATCH 038/112] Remove gemnasium badge (#140) * Remove gemnasium badge * Add changelog.md entry --- CHANGELOG.md | 1 + README.md | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa207d4e..77ac6b9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ * Fix token leak via referrer header [#56](https://github.com/Sorcery/sorcery/pull/56) * Add `login_user` helper for request specs [#57](https://github.com/Sorcery/sorcery/pull/57) * Added `demodulize` on authentication class name association name fetch [#147](https://github.com/Sorcery/sorcery/pull/147) +* Remove Gemnasium badge [#140](https://github.com/Sorcery/sorcery/pull/140) ## 0.11.0 diff --git a/README.md b/README.md index 73da614d..0ee09911 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ [![Gem Version](https://badge.fury.io/rb/sorcery.svg)](https://rubygems.org/gems/sorcery) [![Gem Downloads](https://img.shields.io/gem/dt/sorcery.svg)](https://rubygems.org/gems/sorcery) [![Build Status](https://travis-ci.org/Sorcery/sorcery.svg?branch=master)](https://travis-ci.org/Sorcery/sorcery) -[![Dependency Status](https://gemnasium.com/badges/github.com/Sorcery/sorcery.svg)](https://gemnasium.com/github.com/Sorcery/sorcery) [![Code Climate](https://codeclimate.com/github/Sorcery/sorcery.svg)](https://codeclimate.com/github/Sorcery/sorcery) [![Inline docs](http://inch-ci.org/github/Sorcery/sorcery.svg?branch=master)](http://inch-ci.org/github/Sorcery/sorcery) [![Join the chat at https://gitter.im/Sorcery/sorcery](https://badges.gitter.im/join_chat.svg)](https://gitter.im/Sorcery/sorcery?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) From f9e159fe095b7856952445262e63e1dfd2613df4 Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Thu, 20 Sep 2018 12:36:24 -0700 Subject: [PATCH 039/112] Fix changelog entries (#148) * Move changelog entries for recent pulls to HEAD instead of v0.12.0 * Add missing changelog entries for PRs since v0.12.0 --- CHANGELOG.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77ac6b9d..bd643688 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,16 @@ # Changelog ## HEAD +* Add support for Rails 5.2 / Ruby 2.5 [#129](https://github.com/Sorcery/sorcery/pull/129) +* Fix migration files not being generated [#128](https://github.com/Sorcery/sorcery/pull/128) +* Add support for ActionController::API [#133](https://github.com/Sorcery/sorcery/pull/133) +* Update activation email to use after_commit callback [#130](https://github.com/Sorcery/sorcery/pull/130) +* Add opt-in `invalidate_active_sessions!` method [#110](https://github.com/Sorcery/sorcery/pull/110) +* Pass along `remember_me` to `#auto_login` [#136](https://github.com/Sorcery/sorcery/pull/136) +* Respect SessionTimeout on login via RememberMe [#102](https://github.com/Sorcery/sorcery/pull/102) +* Added `demodulize` on authentication class name association name fetch [#147](https://github.com/Sorcery/sorcery/pull/147) +* Remove Gemnasium badge [#140](https://github.com/Sorcery/sorcery/pull/140) + ## 0.12.0 * Fix magic_login not inheriting from migration_class_name [#99](https://github.com/Sorcery/sorcery/pull/99) @@ -18,8 +28,6 @@ * Add support for VK OAuth (thanks to @Hirurg103) [#109](https://github.com/Sorcery/sorcery/pull/109) * Fix token leak via referrer header [#56](https://github.com/Sorcery/sorcery/pull/56) * Add `login_user` helper for request specs [#57](https://github.com/Sorcery/sorcery/pull/57) -* Added `demodulize` on authentication class name association name fetch [#147](https://github.com/Sorcery/sorcery/pull/147) -* Remove Gemnasium badge [#140](https://github.com/Sorcery/sorcery/pull/140) ## 0.11.0 From 0e21ede9cec661046fbbcbdb8ec960cdb47c0bec Mon Sep 17 00:00:00 2001 From: Wayne Nabors Date: Thu, 20 Sep 2018 17:02:45 -0500 Subject: [PATCH 040/112] Add Instagram Provider (#51) * Add Instagram Provider * Changed how scope is set * Code style for unused method argument * changed parse attr to read/write * add facebook.parse to config * Add changelog entry --- CHANGELOG.md | 1 + .../sorcery/templates/initializer.rb | 6 ++ lib/sorcery/controller/submodules/external.rb | 1 + lib/sorcery/providers/instagram.rb | 78 +++++++++++++++++++ spec/controllers/controller_oauth2_spec.rb | 29 +++++-- .../app/controllers/sorcery_controller.rb | 20 +++++ spec/rails_app/config/routes.rb | 3 + 7 files changed, 133 insertions(+), 5 deletions(-) create mode 100644 lib/sorcery/providers/instagram.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index bd643688..bc57fc92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * Respect SessionTimeout on login via RememberMe [#102](https://github.com/Sorcery/sorcery/pull/102) * Added `demodulize` on authentication class name association name fetch [#147](https://github.com/Sorcery/sorcery/pull/147) * Remove Gemnasium badge [#140](https://github.com/Sorcery/sorcery/pull/140) +* Add Instragram provider [#51](https://github.com/Sorcery/sorcery/pull/51) ## 0.12.0 diff --git a/lib/generators/sorcery/templates/initializer.rb b/lib/generators/sorcery/templates/initializer.rb index 65f3fcbe..115c30e3 100644 --- a/lib/generators/sorcery/templates/initializer.rb +++ b/lib/generators/sorcery/templates/initializer.rb @@ -124,6 +124,12 @@ # config.facebook.api_version = "v2.3" # config.facebook.parse = :json # + # config.instagram.key = "" + # config.instagram.secret = "" + # config.instagram.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=instagram" + # config.instagram.user_info_mapping = {:email => "username"} + # config.instagram.access_permissions = ["basic", "public_content", "follower_list", "comments", "relationships", "likes"] + # # config.github.key = "" # config.github.secret = "" # config.github.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=github" diff --git a/lib/sorcery/controller/submodules/external.rb b/lib/sorcery/controller/submodules/external.rb index fcff2d30..3c78aca8 100644 --- a/lib/sorcery/controller/submodules/external.rb +++ b/lib/sorcery/controller/submodules/external.rb @@ -23,6 +23,7 @@ def self.included(base) require 'sorcery/providers/slack' require 'sorcery/providers/wechat' require 'sorcery/providers/microsoft' + require 'sorcery/providers/instagram' Config.module_eval do class << self diff --git a/lib/sorcery/providers/instagram.rb b/lib/sorcery/providers/instagram.rb new file mode 100644 index 00000000..cb6d42d1 --- /dev/null +++ b/lib/sorcery/providers/instagram.rb @@ -0,0 +1,78 @@ +module Sorcery + module Providers + # This class adds support for OAuth with Instagram.com. + + class Instagram < Base + + include Protocols::Oauth2 + + + attr_accessor :access_permissions, :token_url, + :authorization_path, :user_info_path, + :scope, :user_info_fields + + + def initialize + super + + @site = 'https://api.instagram.com' + @token_url = '/oauth/access_token' + @authorization_path = '/oauth/authorize/' + @user_info_path = '/v1/users/self' + @scope = 'basic' + end + + def self.included(base) + base.extend Sorcery::Providers + end + + # provider implements method to build Oauth client + def login_url(_params, _session) + authorize_url(token_url: @token_url) + end + + + # overrides oauth2#authorize_url to allow customized scope. + def authorize_url(opts = {}) + @scope = access_permissions.present? ? access_permissions.join(' ') : scope + super(opts.merge(:token_url => @token_url)) + end + + # pass oauth2 param `code` provided by instgrm server + def process_callback(params, _session) + args = {}.tap do |a| + a[:code] = params[:code] if params[:code] + end + get_access_token(args, token_url: @token_url, + client_id: @key, client_secret: @secret) + end + + + # see `user_info_mapping` in config/initializer, + # given `user_info_mapping` to specify + # {:db_attribute_name => 'instagram_attr_name'} + # so that Sorcery can build AR model from attr names + # + # NOTE: instead of just getting the user info + # from the access_token (which already returns them), + # testing strategy relies on querying user_info_path + def get_user_hash(access_token) + call_api_params = { + :access_token => access_token.token, + :client_id => access_token[:client_id] + } + response = access_token.get( + "#{user_info_path}?#{call_api_params.to_param}" + ) + + _user_attrs = Hash.new + _user_attrs[:user_info] = JSON.parse(response.body)['data'] + _user_attrs[:uid] = _user_attrs[:user_info]['id'] + _user_attrs + end + + end + + end + +end diff --git a/spec/controllers/controller_oauth2_spec.rb b/spec/controllers/controller_oauth2_spec.rb index 4ac6bb75..4702e6b8 100644 --- a/spec/controllers/controller_oauth2_spec.rb +++ b/spec/controllers/controller_oauth2_spec.rb @@ -155,7 +155,7 @@ expect(flash[:notice]).to eq 'Success!' end - [:github, :google, :liveid, :vk, :salesforce, :paypal, :slack, :wechat, :microsoft].each do |provider| + [:github, :google, :liveid, :vk, :salesforce, :paypal, :slack, :wechat, :microsoft, :instagram].each do |provider| describe "with #{provider}" do it 'login_at redirects correctly' do get :"login_at_test_#{provider}" @@ -201,7 +201,7 @@ describe 'OAuth with User Activation features' do before(:all) do sorcery_reload!([:user_activation,:external], :user_activation_mailer => ::SorceryMailer) - sorcery_controller_property_set(:external_providers, [:facebook, :github, :google, :liveid, :vk, :salesforce, :paypal, :slack, :wechat, :microsoft]) + sorcery_controller_property_set(:external_providers, [:facebook, :github, :google, :liveid, :vk, :salesforce, :paypal, :slack, :wechat, :microsoft, :instagram]) # TODO: refactor sorcery_controller_external_property_set(:facebook, :key, "eYVNBjBDi33aa9GkA3w") @@ -234,6 +234,9 @@ sorcery_controller_external_property_set(:microsoft, :key, "eYVNBjBDi33aa9GkA3w") sorcery_controller_external_property_set(:microsoft, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") sorcery_controller_external_property_set(:microsoft, :callback_url, "http://blabla.com") + sorcery_controller_external_property_set(:instagram, :key, "eYVNBjBDi33aa9GkA3w") + sorcery_controller_external_property_set(:instagram, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") + sorcery_controller_external_property_set(:instagram, :callback_url, "http://blabla.com") end after(:each) do @@ -256,7 +259,7 @@ expect(ActionMailer::Base.deliveries.size).to eq old_size end - [:github, :google, :liveid, :vk, :salesforce, :paypal, :wechat, :microsoft].each do |provider| + [:github, :google, :liveid, :vk, :salesforce, :paypal, :wechat, :microsoft, :instagram].each do |provider| it "does not send activation email to external users (#{provider})" do old_size = ActionMailer::Base.deliveries.size create_new_external_user provider @@ -355,6 +358,8 @@ def stub_all_oauth2_requests! access_token = double(OAuth2::AccessToken) allow(access_token).to receive(:token_param=) + # Needed for Instagram + allow(access_token).to receive(:[]).with(:client_id){"eYVNBjBDi33aa9GkA3w"} response = double(OAuth2::Response) allow(response).to receive(:body) { { @@ -389,6 +394,16 @@ def stub_all_oauth2_requests! }, # response for wechat auth 'unionid' => '123', + # response for instagram + 'data' => { + 'username' => 'pnmahoney', + 'bio' => 'turn WHAT down?', + 'website' => '', + 'profile_picture' => 'http://photos-d.ak.instagram.com/hphotos-ak-xpa1/10454121_417985815007395_867850883_a.jpg', + 'full_name' => 'Patrick Mahoney', + 'counts' => {'media' => 2, 'followed_by' => 100, 'follows' => 71}, + 'id'=>'123' + } }.to_json } allow(access_token).to receive(:get) { response } allow(access_token).to receive(:token) { '187041a618229fdaf16613e96e1caabc1e86e46bbfad228de41520e63fe45873684c365a14417289599f3' } @@ -398,7 +413,7 @@ def stub_all_oauth2_requests! end def set_external_property - sorcery_controller_property_set(:external_providers, [:facebook, :github, :google, :liveid, :vk, :salesforce, :paypal, :slack, :wechat, :microsoft]) + sorcery_controller_property_set(:external_providers, [:facebook, :github, :google, :liveid, :vk, :salesforce, :paypal, :slack, :wechat, :microsoft, :instagram]) sorcery_controller_external_property_set(:facebook, :key, "eYVNBjBDi33aa9GkA3w") sorcery_controller_external_property_set(:facebook, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") sorcery_controller_external_property_set(:facebook, :callback_url, "http://blabla.com") @@ -429,6 +444,9 @@ def set_external_property sorcery_controller_external_property_set(:microsoft, :key, "eYVNBjBDi33aa9GkA3w") sorcery_controller_external_property_set(:microsoft, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") sorcery_controller_external_property_set(:microsoft, :callback_url, "http://blabla.com") + sorcery_controller_external_property_set(:instagram, :key, "eYVNBjBDi33aa9GkA3w") + sorcery_controller_external_property_set(:instagram, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") + sorcery_controller_external_property_set(:instagram, :callback_url, "http://blabla.com") end def provider_url(provider) @@ -441,7 +459,8 @@ def provider_url(provider) salesforce: "https://login.salesforce.com/services/oauth2/authorize?client_id=#{::Sorcery::Controller::Config.salesforce.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope#{'=' + ::Sorcery::Controller::Config.salesforce.scope unless ::Sorcery::Controller::Config.salesforce.scope.nil?}&state", slack: "https://slack.com/oauth/authorize?client_id=#{::Sorcery::Controller::Config.slack.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=identity.basic%2C+identity.email&state", wechat: "https://open.weixin.qq.com/connect/qrconnect?appid=#{::Sorcery::Controller::Config.wechat.key}&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=snsapi_login&state=#wechat_redirect", - microsoft: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=#{::Sorcery::Controller::Config.microsoft.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=openid+email+https%3A%2F%2Fgraph.microsoft.com%2FUser.Read&state" + microsoft: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=#{::Sorcery::Controller::Config.microsoft.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=openid+email+https%3A%2F%2Fgraph.microsoft.com%2FUser.Read&state", + instagram: "https://api.instagram.com/oauth/authorize?client_id=#{::Sorcery::Controller::Config.instagram.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=#{::Sorcery::Controller::Config.instagram.scope}&state" }[provider] end end diff --git a/spec/rails_app/app/controllers/sorcery_controller.rb b/spec/rails_app/app/controllers/sorcery_controller.rb index 0d77fae0..938e85be 100644 --- a/spec/rails_app/app/controllers/sorcery_controller.rb +++ b/spec/rails_app/app/controllers/sorcery_controller.rb @@ -141,6 +141,10 @@ def login_at_test_with_state login_at(:facebook, state: 'bla') end + def login_at_test_instagram + login_at(:instagram) + end + def test_login_from_twitter if @user = login_from(:twitter) redirect_to 'bla', notice: 'Success!' @@ -239,6 +243,14 @@ def test_login_from_slack end end + def test_login_from_instagram + if @user = login_from(:instagram) + redirect_to 'bla', notice: 'Success!' + else + redirect_to 'blu', alert: 'Failed!' + end + end + def test_return_to_with_external_twitter if @user = login_from(:twitter) redirect_back_or_to 'bla', notice: 'Success!' @@ -337,6 +349,14 @@ def test_return_to_with_external_slack end end + def test_return_to_with_external_instagram + if @user = login_from(:instagram) + redirect_back_or_to 'bla', notice: 'Success!' + else + redirect_to 'blu', alert: 'Failed!' + end + end + def test_create_from_provider provider = params[:provider] login_from(provider) diff --git a/spec/rails_app/config/routes.rb b/spec/rails_app/config/routes.rb index 2f63fa99..de1b80d8 100644 --- a/spec/rails_app/config/routes.rb +++ b/spec/rails_app/config/routes.rb @@ -30,6 +30,7 @@ get :test_login_from_jira get :test_login_from_salesforce get :test_login_from_slack + get :test_login_from_instagram get :login_at_test get :login_at_test_twitter get :login_at_test_facebook @@ -43,6 +44,7 @@ get :login_at_test_jira get :login_at_test_salesforce get :login_at_test_slack + get :login_at_test_instagram get :test_return_to_with_external get :test_return_to_with_external_twitter get :test_return_to_with_external_facebook @@ -56,6 +58,7 @@ get :test_return_to_with_external_jira get :test_return_to_with_external_salesforce get :test_return_to_with_external_slack + get :test_return_to_with_external_instagram get :test_http_basic_auth get :some_action_making_a_non_persisted_change_to_the_user post :test_login_with_remember From ee532c1ada55490533e5afa14d434a272c15c0d5 Mon Sep 17 00:00:00 2001 From: hetakan <38922317+hetakan@users.noreply.github.com> Date: Sat, 22 Sep 2018 06:51:38 +0900 Subject: [PATCH 041/112] Remove `publish_actions` permission for facebook (#139) * Remove `publish_actions` permission from template * Add a change log entry for PR #139 --- CHANGELOG.md | 1 + lib/generators/sorcery/templates/initializer.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc57fc92..ef446e4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * Added `demodulize` on authentication class name association name fetch [#147](https://github.com/Sorcery/sorcery/pull/147) * Remove Gemnasium badge [#140](https://github.com/Sorcery/sorcery/pull/140) * Add Instragram provider [#51](https://github.com/Sorcery/sorcery/pull/51) +* Remove `publish_actions` permission for facebook [#139](https://github.com/Sorcery/sorcery/pull/139) ## 0.12.0 diff --git a/lib/generators/sorcery/templates/initializer.rb b/lib/generators/sorcery/templates/initializer.rb index 115c30e3..e08c3bcc 100644 --- a/lib/generators/sorcery/templates/initializer.rb +++ b/lib/generators/sorcery/templates/initializer.rb @@ -119,7 +119,7 @@ # config.facebook.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=facebook" # config.facebook.user_info_path = "me?fields=email" # config.facebook.user_info_mapping = {:email => "email"} - # config.facebook.access_permissions = ["email", "publish_actions"] + # config.facebook.access_permissions = ["email"] # config.facebook.display = "page" # config.facebook.api_version = "v2.3" # config.facebook.parse = :json From e3045a94bbb02637babc159f92ea5540672a616f Mon Sep 17 00:00:00 2001 From: Titus Ramczykowski Date: Mon, 15 Oct 2018 20:45:11 +0200 Subject: [PATCH 042/112] Fix rails injection (#150) * fix rails injection * only add helper_methods to ActionController::Base --- lib/sorcery/engine.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/sorcery/engine.rb b/lib/sorcery/engine.rb index 77be7648..24e1ecd1 100644 --- a/lib/sorcery/engine.rb +++ b/lib/sorcery/engine.rb @@ -8,8 +8,10 @@ class Engine < Rails::Engine config.sorcery = ::Sorcery::Controller::Config initializer 'extend Controller with sorcery' do - ActiveSupport.on_load('action_controller') do + ActiveSupport.on_load(:action_controller) do send(:include, Sorcery::Controller) + end + ActiveSupport.on_load(:action_controller_base) do helper_method :current_user helper_method :logged_in? end From 6c155be1319a8699eb05f51c2bcba5dc63dd94df Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Thu, 8 Nov 2018 21:47:56 +0000 Subject: [PATCH 043/112] Prepare for 1.0.0 (#157) * Remove jruby references jruby was added previously, but never fully supported. Removing for now, pending proper support. * Match min ruby requirement with lowest tested ruby version * Add maintainer to gemspec author list * Update license to md format * Remove excess whitespace * Update rubocop ruby lock and regen TODO list * Disable rubocop for migration files (breaks) * Fix Bundler/OrderedGems * Fix rails 6 deprecation warning * Add missing newline * Fix Gemspec/OrderedDependencies * Fix Gemspec/RequiredRubyVersion and regen TODO * Fix Layout/AlignHash * Fix Layout/ClosingParenthesisIndentation * Fix Layout/EmptyLineAfterGuardClause * Fix Layout/EmptyLineBetweenDefs * Fix Layout/EmptyLines Fixed by last commit * Fix Layout/EmptyLinesAroundArguments * Fix Layout/EmptyLinesAroundBlockBody * Fix Layout/EmptyLinesAroundClassBody * Fixed Layout/EmptyLinesAroundModuleBody on previous commit * Fix Layout/EndAlignment * Fix Layout/ExtraSpacing * Fix Layout/FirstParameterIndentation * Fix Layout/IndentHash * Fix Layout/MultilineMethodCallBraceLayout * Fix Layout/MultilineMethodCallIndentation * Fix Layout/MultilineOperationIndentation * Fix Layout/SpaceAfterComma * Fix Layout/SpaceAroundEqualsInParameterDefault * Fix Layout/SpaceAroundOperators * Fix Layout/SpaceBeforeBlockBraces * Fix Layout/SpaceInsideBlockBraces * Fix Layout/SpaceInsideHashLiteralBraces * Fix Layout/TrailingBlankLines * Fix Layout/TrailingWhitespace * Fix Lint/AmbiguousBlockAssociation * Fix Lint/AssignmentInCondition * Fix Lint/DuplicateMethods * Add rubocop to development dependencies * Rails 4.1 has been released, remove rescue statement * Fix Lint/HandleExceptions * Fix Lint/NonLocalExitFromIterator * Fix Lint/ParenthesesAsGroupedExpression * Fix Lint/UnderscorePrefixedVariableName * Fix Lint/UselessAssignment * Fix Lint/Void - Found potentially broken specs for remember_me functionality * Fix Naming/FileName * Fix Style/AndOr * Fix Style/BracesAroundHashParameters * Add Github issue template * Fix Style/TrailingCommaInHashLiteral * Fix Style/TrailingCommaInArguments * Fix Style/SymbolArray * Fix Style/StringLiterals * Fix Style/RescueStandardError * Fix Style/RegexpLiteral * Fix Style/RedundantSelf * Fix Style/RedundantReturn * Fix Style/RedundantParentheses * Move harder to solve rubocop issues to prepare for 1.0.0 * Naming conventions will be fixed by rewrite in 1.0.0 * Fix Naming/MemoizedInstanceVariableName * Naming conventions will be fixed by rewrite in 1.0.0 * Fix Style/PercentLiteralDelimiters * Fix Style/ParenthesesAroundCondition Fixed by previous commit * Fix Style/NumericLiterals * Fix Style/MutableConstant * Fix Style/MultipleComparison * Fix Style/IfUnlessModifier Fixed on previous commit * Fix Style/HashSyntax * Fix Style/GuardClause * Fix Style/FormatString * Fix Style/ExpandPathArguments * Fix Style/EvalWithLocation * Fix Style/EmptyLiteral * Disable Style/DoubleNegation * Documentation will be fixed by rewrite in 1.0.0 * Fix Style/Dir * Fix Style/ConditionalAssignment Fixed by previous commit * Fix Style/ClassAndModuleChildren * Regenerate rubocop TODO * Add maintainer to gemspec email list --- .github/ISSUE_TEMPLATE.md | 20 + .rubocop.yml | 54 ++- .rubocop_todo.yml | 430 +----------------- .travis.yml | 32 +- Gemfile | 2 +- LICENSE.txt => LICENSE.md | 2 +- ...gemfile => active_record_rails_40.gemfile} | 3 +- ...gemfile => active_record_rails_41.gemfile} | 3 +- ...gemfile => active_record_rails_42.gemfile} | 3 +- lib/generators/sorcery/USAGE | 2 +- lib/generators/sorcery/install_generator.rb | 40 +- lib/sorcery/adapters/mongoid_adapter.rb | 24 +- lib/sorcery/controller.rb | 32 +- .../controller/submodules/activity_logging.rb | 4 + lib/sorcery/controller/submodules/external.rb | 68 +-- .../controller/submodules/http_basic_auth.rb | 1 + .../controller/submodules/session_timeout.rb | 2 + lib/sorcery/crypto_providers/aes256.rb | 1 + lib/sorcery/crypto_providers/bcrypt.rb | 3 +- lib/sorcery/model.rb | 15 +- lib/sorcery/model/config.rb | 6 +- .../submodules/brute_force_protection.rb | 13 +- lib/sorcery/model/submodules/external.rb | 3 +- lib/sorcery/model/submodules/magic_login.rb | 65 ++- .../model/submodules/reset_password.rb | 3 +- lib/sorcery/protocols/oauth.rb | 1 + lib/sorcery/providers/heroku.rb | 1 + lib/sorcery/providers/instagram.rb | 35 +- lib/sorcery/providers/linkedin.rb | 2 +- lib/sorcery/providers/vk.rb | 2 +- lib/sorcery/providers/wechat.rb | 14 +- lib/sorcery/test_helpers/internal.rb | 9 +- lib/sorcery/test_helpers/internal/rails.rb | 22 +- lib/sorcery/version.rb | 2 +- sorcery.gemspec | 34 +- spec/active_record/user_magic_login_spec.rb | 4 +- spec/controllers/controller_oauth2_spec.rb | 293 +++++++----- spec/controllers/controller_oauth_spec.rb | 10 +- .../controller_remember_me_spec.rb | 15 +- .../controller_session_timeout_spec.rb | 18 +- spec/controllers/controller_spec.rb | 2 +- spec/providers/vk_spec.rb | 25 +- .../app/controllers/sorcery_controller.rb | 74 +-- spec/rails_app/app/mailers/sorcery_mailer.rb | 2 +- spec/rails_app/config/application.rb | 7 +- spec/rails_app/config/boot.rb | 2 +- spec/rails_app/config/environment.rb | 2 +- ...224223624_add_activity_logging_to_users.rb | 4 +- ...20170924151831_add_magic_login_to_users.rb | 6 +- spec/rails_app/db/schema.rb | 16 +- .../user_magic_login_shared_examples.rb | 100 ++-- .../user_oauth_shared_examples.rb | 2 +- .../user_remember_me_shared_examples.rb | 2 +- .../user_reset_password_shared_examples.rb | 6 +- spec/shared_examples/user_shared_examples.rb | 35 +- spec/sorcery_crypto_providers_spec.rb | 2 +- spec/spec.opts | 2 +- spec/spec_helper.rb | 2 +- 58 files changed, 658 insertions(+), 926 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE.md rename LICENSE.txt => LICENSE.md (94%) rename gemfiles/{active_record-rails40.gemfile => active_record_rails_40.gemfile} (64%) rename gemfiles/{active_record-rails41.gemfile => active_record_rails_41.gemfile} (64%) rename gemfiles/{active_record-rails42.gemfile => active_record_rails_42.gemfile} (64%) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..582e1fac --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,20 @@ +Please complete all sections. + +### Configuration + +- Sorcery Version: `` +- Ruby Version: `` +- Framework: `` +- Platform: `` + +### Expected Behavior + +Tell us what should happen. + +### Actual Behavior + +Tell us what happens instead. + +### Steps to Reproduce + +Please list all steps to reproduce the issue. diff --git a/.rubocop.yml b/.rubocop.yml index 67036565..ce1dc552 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,5 +1,55 @@ -# Use auto generated file to ignore existing warnings. -inherit_from: '.rubocop_todo.yml' +inherit_from: .rubocop_todo.yml AllCops: + Exclude: + - 'lib/generators/sorcery/templates/**/*' TargetRubyVersion: 2.2 + +# See: https://github.com/rubocop-hq/rubocop/issues/3344 +Style/DoubleNegation: + Enabled: false + +#################### +## Pre-1.0.0 Code ## +#################### + +Metrics/AbcSize: + Exclude: + - 'lib/**/*' + - 'spec/**/*' +Metrics/BlockLength: + Exclude: + - 'lib/**/*' + - 'spec/**/*' +Metrics/LineLength: + Exclude: + - 'lib/**/*' + - 'spec/**/*' +Metrics/ClassLength: + Exclude: + - 'lib/**/*' + - 'spec/**/*' +Metrics/CyclomaticComplexity: + Exclude: + - 'lib/**/*' + - 'spec/**/*' +Metrics/MethodLength: + Exclude: + - 'lib/**/*' + - 'spec/**/*' +Metrics/PerceivedComplexity: + Exclude: + - 'lib/**/*' + - 'spec/**/*' +Naming/AccessorMethodName: + Exclude: + - 'lib/**/*' + - 'spec/**/*' +Naming/PredicateName: + Exclude: + - 'lib/**/*' + - 'spec/**/*' +Style/Documentation: + Exclude: + - 'lib/**/*' + - 'spec/**/*' diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index c539bacd..4d8c565f 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,435 +1,7 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2018-02-03 14:20:34 -0800 using RuboCop version 0.51.0. +# on 2018-11-01 18:13:47 -0700 using RuboCop version 0.59.2. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. - -# Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: Include, TreatCommentsAsGroupSeparators. -# Include: **/Gemfile, **/gems.rb -Bundler/OrderedGems: - Exclude: - - 'Gemfile' - -# Offense count: 5 -# Cop supports --auto-correct. -# Configuration parameters: Include, TreatCommentsAsGroupSeparators. -# Include: **/*.gemspec -Gemspec/OrderedDependencies: - Exclude: - - 'sorcery.gemspec' - -# Offense count: 7 -# Cop supports --auto-correct. -Layout/EmptyLines: - Exclude: - - 'lib/generators/sorcery/templates/initializer.rb' - -# Offense count: 2 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: empty_lines, no_empty_lines -Layout/EmptyLinesAroundBlockBody: - Exclude: - - 'spec/rails_app/db/schema.rb' - -# Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines -Layout/EmptyLinesAroundClassBody: - Exclude: - - 'lib/sorcery/adapters/mongoid_adapter.rb' - -# Offense count: 2 -# Cop supports --auto-correct. -# Configuration parameters: AllowForAlignment, ForceEqualSignAlignment. -Layout/ExtraSpacing: - Exclude: - - 'lib/sorcery/adapters/mongoid_adapter.rb' - -# Offense count: 3 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. -# SupportedStyles: special_inside_parentheses, consistent, align_braces -Layout/IndentHash: - Exclude: - - 'lib/sorcery/model/submodules/magic_login.rb' - - 'lib/sorcery/providers/wechat.rb' - -# Offense count: 2 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. -# SupportedStyles: aligned, indented, indented_relative_to_receiver -Layout/MultilineMethodCallIndentation: - Exclude: - - 'lib/sorcery/adapters/mongoid_adapter.rb' - -# Offense count: 2 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth. -# SupportedStyles: aligned, indented -Layout/MultilineOperationIndentation: - Exclude: - - 'lib/sorcery/model/submodules/magic_login.rb' - -# Offense count: 2 -# Cop supports --auto-correct. -Layout/SpaceAfterComma: - Exclude: - - 'lib/sorcery/adapters/mongoid_adapter.rb' - - 'spec/controllers/controller_oauth2_spec.rb' - -# Offense count: 2 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: space, no_space -Layout/SpaceAroundEqualsInParameterDefault: - Exclude: - - 'lib/sorcery/adapters/mongoid_adapter.rb' - -# Offense count: 3 -# Cop supports --auto-correct. -# Configuration parameters: AllowForAlignment. -Layout/SpaceAroundOperators: - Exclude: - - 'lib/sorcery/adapters/mongoid_adapter.rb' - - 'spec/shared_examples/user_magic_login_shared_examples.rb' - -# Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, SupportedStylesForEmptyBraces. -# SupportedStyles: space, no_space -# SupportedStylesForEmptyBraces: space, no_space -Layout/SpaceBeforeBlockBraces: - Exclude: - - 'lib/sorcery/providers/linkedin.rb' - -# Offense count: 8 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces, SpaceBeforeBlockParameters. -# SupportedStyles: space, no_space -# SupportedStylesForEmptyBraces: space, no_space -Layout/SpaceInsideBlockBraces: - Exclude: - - 'lib/sorcery/adapters/mongoid_adapter.rb' - - 'lib/sorcery/providers/linkedin.rb' - - 'spec/shared_examples/user_magic_login_shared_examples.rb' - -# Offense count: 4 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces. -# SupportedStyles: space, no_space, compact -# SupportedStylesForEmptyBraces: space, no_space -Layout/SpaceInsideHashLiteralBraces: - Exclude: - - 'lib/sorcery/adapters/mongoid_adapter.rb' - - 'lib/sorcery/model/submodules/magic_login.rb' - -# Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: final_newline, final_blank_line -Layout/TrailingBlankLines: - Exclude: - - 'lib/sorcery/providers/wechat.rb' - -# Offense count: 52 -# Cop supports --auto-correct. -Layout/TrailingWhitespace: - Exclude: - - 'lib/sorcery/model/submodules/magic_login.rb' - - 'spec/active_record/user_magic_login_spec.rb' - - 'spec/rails_app/app/mailers/sorcery_mailer.rb' - - 'spec/rails_app/db/migrate/magic_login/20170924151831_add_magic_login_to_users.rb' - - 'spec/shared_examples/user_magic_login_shared_examples.rb' - -# Offense count: 1 -Lint/AmbiguousBlockAssociation: - Exclude: - - 'spec/shared_examples/user_shared_examples.rb' - -# Offense count: 28 -# Configuration parameters: AllowSafeAssignment. -Lint/AssignmentInCondition: - Exclude: - - 'lib/sorcery/controller/submodules/external.rb' - - 'lib/sorcery/providers/vk.rb' - - 'spec/rails_app/app/controllers/sorcery_controller.rb' - -# Offense count: 1 -Lint/DuplicateMethods: - Exclude: - - 'lib/sorcery/model/config.rb' - -# Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyleAlignWith, SupportedStylesAlignWith, AutoCorrect. -# SupportedStylesAlignWith: keyword, variable, start_of_line -Lint/EndAlignment: - Exclude: - - 'lib/sorcery/model/config.rb' - -# Offense count: 5 -Lint/HandleExceptions: - Exclude: - - 'lib/sorcery/controller.rb' - - 'lib/sorcery/model.rb' - - 'spec/rails_app/config/application.rb' - - 'spec/shared_examples/user_shared_examples.rb' - -# Offense count: 1 -Lint/NonLocalExitFromIterator: - Exclude: - - 'lib/sorcery/controller.rb' - -# Offense count: 1 -Lint/ParenthesesAsGroupedExpression: - Exclude: - - 'spec/shared_examples/user_remember_me_shared_examples.rb' - -# Offense count: 3 -Lint/RescueWithoutErrorClass: - Exclude: - - 'lib/sorcery/controller/submodules/external.rb' - - 'spec/shared_examples/user_shared_examples.rb' - - 'spec/spec_helper.rb' - -# Offense count: 7 -Lint/UselessAssignment: - Exclude: - - 'lib/sorcery/controller/submodules/external.rb' - - 'lib/sorcery/model/submodules/external.rb' - - 'spec/controllers/controller_oauth2_spec.rb' - - 'spec/controllers/controller_remember_me_spec.rb' - -# Offense count: 2 -Lint/Void: - Exclude: - - 'spec/controllers/controller_remember_me_spec.rb' - -# Offense count: 28 -Metrics/AbcSize: - Max: 36 - -# Offense count: 73 -# Configuration parameters: CountComments, ExcludedMethods. -Metrics/BlockLength: - Max: 391 - -# Offense count: 1 -# Configuration parameters: CountComments. -Metrics/ClassLength: - Max: 303 - -# Offense count: 3 -Metrics/CyclomaticComplexity: - Max: 9 - -# Offense count: 833 -# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns. -# URISchemes: http, https -Metrics/LineLength: - Max: 323 - -# Offense count: 33 -# Configuration parameters: CountComments. -Metrics/MethodLength: - Max: 39 - -# Offense count: 1 -Metrics/PerceivedComplexity: - Max: 9 - -# Offense count: 11 -Naming/AccessorMethodName: - Exclude: - - 'lib/sorcery/adapters/mongoid_adapter.rb' - - 'lib/sorcery/controller/submodules/remember_me.rb' - - 'lib/sorcery/model/submodules/activity_logging.rb' - - 'lib/sorcery/protocols/oauth.rb' - - 'lib/sorcery/providers/jira.rb' - - 'lib/sorcery/providers/linkedin.rb' - - 'lib/sorcery/providers/twitter.rb' - - 'lib/sorcery/providers/xing.rb' - -# Offense count: 2 -# Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist, MethodDefinitionMacros. -# NamePrefix: is_, has_, have_ -# NamePrefixBlacklist: is_, has_, have_ -# NameWhitelist: is_a? -# MethodDefinitionMacros: define_method, define_singleton_method -Naming/PredicateName: - Exclude: - - 'spec/**/*' - - 'lib/sorcery/model/submodules/remember_me.rb' - - 'lib/sorcery/providers/base.rb' - -# Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: always, conditionals -Style/AndOr: - Exclude: - - 'lib/sorcery/model/submodules/magic_login.rb' - -# Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: braces, no_braces, context_dependent -Style/BracesAroundHashParameters: - Exclude: - - 'lib/sorcery/model/submodules/magic_login.rb' - -# Offense count: 1 -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: nested, compact -Style/ClassAndModuleChildren: - Exclude: - - 'lib/sorcery/test_helpers/internal.rb' - -# Offense count: 2 -Style/DateTime: - Exclude: - - 'spec/shared_examples/user_magic_login_shared_examples.rb' - -# Offense count: 1 -# Cop supports --auto-correct. -Style/Dir: - Exclude: - - 'lib/sorcery/controller/submodules/external.rb' - -# Offense count: 52 -Style/Documentation: - Enabled: false - -# Offense count: 2 -Style/DoubleNegation: - Exclude: - - 'lib/generators/sorcery/helpers.rb' - - 'lib/sorcery/controller.rb' - -# Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles. -# SupportedStyles: format, sprintf, percent -Style/FormatString: - Exclude: - - 'lib/generators/sorcery/install_generator.rb' - -# Offense count: 16 -# Configuration parameters: MinBodyLength. -Style/GuardClause: - Exclude: - - 'lib/generators/sorcery/install_generator.rb' - - 'lib/sorcery/controller.rb' - - 'lib/sorcery/controller/submodules/external.rb' - - 'lib/sorcery/model.rb' - - 'lib/sorcery/model/submodules/brute_force_protection.rb' - - 'lib/sorcery/test_helpers/internal.rb' - - 'lib/sorcery/test_helpers/internal/rails.rb' - - 'spec/rails_app/app/controllers/sorcery_controller.rb' - -# Offense count: 4 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols. -# SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys -Style/HashSyntax: - Exclude: - - 'lib/sorcery/adapters/mongoid_adapter.rb' - - 'spec/controllers/controller_oauth2_spec.rb' - - 'spec/rails_app/db/schema.rb' - -# Offense count: 1 -Style/MultipleComparison: - Exclude: - - 'lib/generators/sorcery/install_generator.rb' - -# Offense count: 2 -# Cop supports --auto-correct. -Style/MutableConstant: - Exclude: - - 'lib/sorcery/test_helpers/internal/rails.rb' - - 'lib/sorcery/version.rb' - -# Offense count: 11 -# Cop supports --auto-correct. -# Configuration parameters: Strict. -Style/NumericLiterals: - MinDigits: 18 - -# Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: AllowSafeAssignment. -Style/ParenthesesAroundCondition: - Exclude: - - 'lib/sorcery/adapters/mongoid_adapter.rb' - -# Offense count: 5 -# Cop supports --auto-correct. -# Configuration parameters: PreferredDelimiters. -Style/PercentLiteralDelimiters: - Exclude: - - 'lib/sorcery/test_helpers/internal/rails.rb' - - 'spec/controllers/controller_oauth2_spec.rb' - - 'spec/rails_app/config/application.rb' - - 'spec/sorcery_crypto_providers_spec.rb' - -# Offense count: 1 -# Cop supports --auto-correct. -Style/RedundantParentheses: - Exclude: - - 'lib/sorcery/adapters/mongoid_adapter.rb' - -# Offense count: 4 -# Cop supports --auto-correct. -Style/RedundantSelf: - Exclude: - - 'lib/sorcery/model/submodules/magic_login.rb' - -# Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes. -# SupportedStyles: slashes, percent_r, mixed -Style/RegexpLiteral: - Exclude: - - 'spec/rails_app/config/application.rb' - -# Offense count: 89 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyle, SupportedStyles, ConsistentQuotesInMultiline. -# SupportedStyles: single_quotes, double_quotes -Style/StringLiterals: - Exclude: - - 'lib/sorcery/adapters/mongoid_adapter.rb' - - 'lib/sorcery/model/submodules/magic_login.rb' - - 'spec/controllers/controller_oauth2_spec.rb' - - 'spec/rails_app/db/schema.rb' - - 'spec/shared_examples/user_magic_login_shared_examples.rb' - -# Offense count: 22 -# Cop supports --auto-correct. -# Configuration parameters: MinSize, SupportedStyles. -# SupportedStyles: percent, brackets -Style/SymbolArray: - EnforcedStyle: brackets - -# Offense count: 1 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyleForMultiline, SupportedStylesForMultiline. -# SupportedStylesForMultiline: comma, consistent_comma, no_comma -Style/TrailingCommaInArguments: - Exclude: - - 'lib/sorcery/providers/wechat.rb' - -# Offense count: 2 -# Cop supports --auto-correct. -# Configuration parameters: EnforcedStyleForMultiline, SupportedStylesForMultiline. -# SupportedStylesForMultiline: comma, consistent_comma, no_comma -Style/TrailingCommaInLiteral: - Exclude: - - 'lib/sorcery/providers/wechat.rb' - - 'spec/controllers/controller_oauth2_spec.rb' diff --git a/.travis.yml b/.travis.yml index 3283ceaf..cd3c6641 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,20 +1,15 @@ language: ruby rvm: - - jruby - 2.2.9 - 2.3.6 - 2.4.3 - 2.5.0 -env: - global: - - JRUBY_OPTS="--2.0" - gemfile: - Gemfile - - gemfiles/active_record-rails40.gemfile - - gemfiles/active_record-rails41.gemfile - - gemfiles/active_record-rails42.gemfile + - gemfiles/active_record_rails_40.gemfile + - gemfiles/active_record_rails_41.gemfile + - gemfiles/active_record_rails_42.gemfile before_script: - mysql -e 'create database sorcery_test;' @@ -24,25 +19,20 @@ before_install: - gem update bundler matrix: - allow_failures: - - rvm: jruby - exclude: - rvm: 2.2.9 - gemfile: gemfiles/active_record-rails40.gemfile + gemfile: gemfiles/active_record_rails_40.gemfile - rvm: 2.3.6 - gemfile: gemfiles/active_record-rails40.gemfile + gemfile: gemfiles/active_record_rails_40.gemfile - rvm: 2.4.3 - gemfile: gemfiles/active_record-rails40.gemfile + gemfile: gemfiles/active_record_rails_40.gemfile - rvm: 2.4.3 - gemfile: gemfiles/active_record-rails41.gemfile + gemfile: gemfiles/active_record_rails_41.gemfile - rvm: 2.4.3 - gemfile: gemfiles/active_record-rails42.gemfile + gemfile: gemfiles/active_record_rails_42.gemfile - rvm: 2.5.0 - gemfile: gemfiles/active_record-rails40.gemfile + gemfile: gemfiles/active_record_rails_40.gemfile - rvm: 2.5.0 - gemfile: gemfiles/active_record-rails41.gemfile + gemfile: gemfiles/active_record_rails_41.gemfile - rvm: 2.5.0 - gemfile: gemfiles/active_record-rails42.gemfile - - rvm: jruby - gemfile: Gemfile + gemfile: gemfiles/active_record_rails_42.gemfile diff --git a/Gemfile b/Gemfile index c7c8f10c..6afda476 100644 --- a/Gemfile +++ b/Gemfile @@ -1,8 +1,8 @@ source 'https://rubygems.org' +gem 'pry' gem 'rails', '~> 5.2.0' gem 'rails-controller-testing' gem 'sqlite3' -gem 'pry' gemspec diff --git a/LICENSE.txt b/LICENSE.md similarity index 94% rename from LICENSE.txt rename to LICENSE.md index cb0d98c0..e43f00c5 100644 --- a/LICENSE.txt +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Copyright (c) 2010 Noam Ben-Ari +Copyright (c) 2010 [Noam Ben-Ari](mailto:nbenari@gmail.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/gemfiles/active_record-rails40.gemfile b/gemfiles/active_record_rails_40.gemfile similarity index 64% rename from gemfiles/active_record-rails40.gemfile rename to gemfiles/active_record_rails_40.gemfile index 9d5aa3e0..ffaf538a 100644 --- a/gemfiles/active_record-rails40.gemfile +++ b/gemfiles/active_record_rails_40.gemfile @@ -1,7 +1,6 @@ source 'https://rubygems.org' -gem 'sqlite3', platform: :mri -gem 'activerecord-jdbcsqlite3-adapter', platform: :jruby gem 'rails', '~> 4.0.1' +gem 'sqlite3', platform: :mri gemspec path: '..' diff --git a/gemfiles/active_record-rails41.gemfile b/gemfiles/active_record_rails_41.gemfile similarity index 64% rename from gemfiles/active_record-rails41.gemfile rename to gemfiles/active_record_rails_41.gemfile index 397ce6a7..67180498 100644 --- a/gemfiles/active_record-rails41.gemfile +++ b/gemfiles/active_record_rails_41.gemfile @@ -1,7 +1,6 @@ source 'https://rubygems.org' -gem 'sqlite3', platform: :mri -gem 'activerecord-jdbcsqlite3-adapter', platform: :jruby gem 'rails', '~> 4.1.0' +gem 'sqlite3', platform: :mri gemspec path: '..' diff --git a/gemfiles/active_record-rails42.gemfile b/gemfiles/active_record_rails_42.gemfile similarity index 64% rename from gemfiles/active_record-rails42.gemfile rename to gemfiles/active_record_rails_42.gemfile index 63c4d0f7..0fec5757 100644 --- a/gemfiles/active_record-rails42.gemfile +++ b/gemfiles/active_record_rails_42.gemfile @@ -1,7 +1,6 @@ source 'https://rubygems.org' -gem 'sqlite3', platform: :mri -gem 'activerecord-jdbcsqlite3-adapter', platform: :jruby gem 'rails', '~> 4.2.0' +gem 'sqlite3', platform: :mri gemspec path: '..' diff --git a/lib/generators/sorcery/USAGE b/lib/generators/sorcery/USAGE index 36b1a263..6e735aac 100644 --- a/lib/generators/sorcery/USAGE +++ b/lib/generators/sorcery/USAGE @@ -1,6 +1,6 @@ Description: Generates the necessary files to get you up and running with Sorcery gem - + Examples: rails generate sorcery:install diff --git a/lib/generators/sorcery/install_generator.rb b/lib/generators/sorcery/install_generator.rb index b03171bb..c3fefa32 100644 --- a/lib/generators/sorcery/install_generator.rb +++ b/lib/generators/sorcery/install_generator.rb @@ -7,7 +7,7 @@ class InstallGenerator < Rails::Generators::Base include Rails::Generators::Migration include Sorcery::Generators::Helpers - source_root File.expand_path('../templates', __FILE__) + source_root File.expand_path('templates', __dir__) argument :submodules, optional: true, type: :array, banner: 'submodules' @@ -21,9 +21,9 @@ class InstallGenerator < Rails::Generators::Base desc: "Specify if you want to add submodules to an existing model\n\t\t\t # (will generate migrations files, and add submodules to config file)" def check_deprecated_options - if options[:migrations] - warn('[DEPRECATED] `--migrations` option is deprecated, please use `--only-submodules` instead') - end + return unless options[:migrations] + + warn('[DEPRECATED] `--migrations` option is deprecated, please use `--only-submodules` instead') end # Copy the initializer file to config/initializers folder. @@ -33,23 +33,22 @@ def copy_initializer_file def configure_initializer_file # Add submodules to the initializer file. - if submodules - submodule_names = submodules.collect { |submodule| ':' + submodule } + return unless submodules - gsub_file sorcery_config_path, /submodules = \[.*\]/ do |str| - current_submodule_names = (str =~ /\[(.*)\]/ ? Regexp.last_match(1) : '').delete(' ').split(',') - "submodules = [#{(current_submodule_names | submodule_names).join(', ')}]" - end + submodule_names = submodules.collect { |submodule| ':' + submodule } + + gsub_file sorcery_config_path, /submodules = \[.*\]/ do |str| + current_submodule_names = (str =~ /\[(.*)\]/ ? Regexp.last_match(1) : '').delete(' ').split(',') + "submodules = [#{(current_submodule_names | submodule_names).join(', ')}]" end end def configure_model # Generate the model and add 'authenticates_with_sorcery!' unless you passed --only-submodules - unless only_submodules? - generate "model #{model_class_name} --skip-migration" + return if only_submodules? - inject_sorcery_to_model - end + generate "model #{model_class_name} --skip-migration" + inject_sorcery_to_model end def inject_sorcery_to_model @@ -62,13 +61,14 @@ def inject_sorcery_to_model def copy_migration_files # Copy core migration file in all cases except when you pass --only-submodules. return unless defined?(ActiveRecord) + migration_template 'migration/core.rb', 'db/migrate/sorcery_core.rb', migration_class_name: migration_class_name unless only_submodules? - if submodules - submodules.each do |submodule| - unless submodule == 'http_basic_auth' || submodule == 'session_timeout' || submodule == 'core' - migration_template "migration/#{submodule}.rb", "db/migrate/sorcery_#{submodule}.rb", migration_class_name: migration_class_name - end + return unless submodules + + submodules.each do |submodule| + unless %w[http_basic_auth session_timeout core].include?(submodule) + migration_template "migration/#{submodule}.rb", "db/migrate/sorcery_#{submodule}.rb", migration_class_name: migration_class_name end end end @@ -79,7 +79,7 @@ def self.next_migration_number(dirname) sleep 1 # make sure each time we get a different timestamp Time.new.utc.strftime('%Y%m%d%H%M%S') else - '%.3d' % (current_migration_number(dirname) + 1) + format('%.3d', (current_migration_number(dirname) + 1)) end end diff --git a/lib/sorcery/adapters/mongoid_adapter.rb b/lib/sorcery/adapters/mongoid_adapter.rb index baace2da..fbf5e23d 100644 --- a/lib/sorcery/adapters/mongoid_adapter.rb +++ b/lib/sorcery/adapters/mongoid_adapter.rb @@ -10,7 +10,7 @@ def update_attributes(attrs) attrs[name] = value.utc if value.is_a?(ActiveSupport::TimeWithZone) @model.send(:"#{name}=", value) end - @model.class.where(:_id => @model.id).update_all(attrs) + @model.class.where(_id: @model.id).update_all(attrs) end def update_attribute(name, value) @@ -23,16 +23,15 @@ def save(options = {}) end def mongoid_4? - Gem::Version.new(::Mongoid::VERSION) >= Gem::Version.new("4.0.0.alpha") + Gem::Version.new(::Mongoid::VERSION) >= Gem::Version.new('4.0.0.alpha') end class << self - - def define_field(name, type, options={}) + def define_field(name, type, options = {}) @klass.field name, options.slice(:default).merge(type: type) end - def define_callback(time, event, method_name, options={}) + def define_callback(time, event, method_name, options = {}) @klass.send callback_name(time, event, options), method_name, options.slice(:if) end @@ -45,7 +44,8 @@ def callback_name(time, event, options) end def credential_regex(credential) - return { :$regex => /^#{Regexp.escape(credential)}$/i } if (@klass.sorcery_config.downcase_username_before_authenticating) + return { :$regex => /^#{Regexp.escape(credential)}$/i } if @klass.sorcery_config.downcase_username_before_authenticating + credential end @@ -81,7 +81,7 @@ def find_by_id(id) end def find_by_username(username) - query = @klass.sorcery_config.username_attribute_names.map {|name| {name => username}} + query = @klass.sorcery_config.username_attribute_names.map { |name| { name => username } } @klass.any_of(*query).first end @@ -95,9 +95,13 @@ def find_by_email(email) def get_current_users config = @klass.sorcery_config - @klass.where(config.last_activity_at_attribute_name.ne => nil) \ - .where("this.#{config.last_logout_at_attribute_name} == null || this.#{config.last_activity_at_attribute_name} > this.#{config.last_logout_at_attribute_name}") \ - .where(config.last_activity_at_attribute_name.gt => config.activity_timeout.seconds.ago.utc).order_by([:_id,:asc]) + @klass.where( + config.last_activity_at_attribute_name.ne => nil + ).where( + "this.#{config.last_logout_at_attribute_name} == null || this.#{config.last_activity_at_attribute_name} > this.#{config.last_logout_at_attribute_name}" + ).where( + config.last_activity_at_attribute_name.gt => config.activity_timeout.seconds.ago.utc + ).order_by(%i[_id asc]) end end end diff --git a/lib/sorcery/controller.rb b/lib/sorcery/controller.rb index 3344ba19..7b40adce 100644 --- a/lib/sorcery/controller.rb +++ b/lib/sorcery/controller.rb @@ -4,11 +4,14 @@ def self.included(klass) klass.class_eval do include InstanceMethods Config.submodules.each do |mod| + # FIXME: Is there a cleaner way to handle missing submodules? + # rubocop:disable Lint/HandleExceptions begin include Submodules.const_get(mod.to_s.split('_').map(&:capitalize).join) rescue NameError # don't stop on a missing submodule. end + # rubocop:enable Lint/HandleExceptions end end Config.update! @@ -20,10 +23,10 @@ module InstanceMethods # Will trigger auto-login attempts via the call to logged_in? # If all attempts to auto-login fail, the failure callback will be called. def require_login - unless logged_in? - session[:return_to_url] = request.url if Config.save_return_to_url && request.get? && !request.xhr? - send(Config.not_authenticated_action) - end + return if logged_in? + + session[:return_to_url] = request.url if Config.save_return_to_url && request.get? && !request.xhr? + send(Config.not_authenticated_action) end # Takes credentials and returns a user on successful authentication. @@ -37,7 +40,10 @@ def login(*credentials) yield(user, failure_reason) if block_given? + # FIXME: Does using `break` or `return nil` change functionality? + # rubocop:disable Lint/NonLocalExitFromIterator return + # rubocop:enable Lint/NonLocalExitFromIterator end old_session = session.dup.to_hash @@ -54,23 +60,19 @@ def login(*credentials) end end - # put this into the catch block to rescue undefined method `destroy_session' - # hotfix for https://github.com/NoamB/sorcery/issues/464 - # can be removed when Rails 4.1 is out def reset_sorcery_session reset_session # protect from session fixation attacks - rescue NoMethodError end # Resets the session and runs hooks before and after. def logout - if logged_in? - user = current_user - before_logout! - @current_user = nil - reset_sorcery_session - after_logout!(user) - end + return unless logged_in? + + user = current_user + before_logout! + @current_user = nil + reset_sorcery_session + after_logout!(user) end def logged_in? diff --git a/lib/sorcery/controller/submodules/activity_logging.rb b/lib/sorcery/controller/submodules/activity_logging.rb index 5aaf578c..eb04cf0b 100644 --- a/lib/sorcery/controller/submodules/activity_logging.rb +++ b/lib/sorcery/controller/submodules/activity_logging.rb @@ -43,6 +43,7 @@ module InstanceMethods # This runs as a hook just after a successful login. def register_login_time_to_db(user, _credentials) return unless Config.register_login_time + user.set_last_login_at(Time.now.in_time_zone) end @@ -50,6 +51,7 @@ def register_login_time_to_db(user, _credentials) # This runs as a hook just before a logout. def register_logout_time_to_db return unless Config.register_logout_time + current_user.set_last_logout_at(Time.now.in_time_zone) end @@ -58,6 +60,7 @@ def register_logout_time_to_db def register_last_activity_time_to_db return unless Config.register_last_activity_time return unless logged_in? + current_user.set_last_activity_at(Time.now.in_time_zone) end @@ -65,6 +68,7 @@ def register_last_activity_time_to_db # This runs as a hook just after a successful login. def register_last_ip_address(_user, _credentials) return unless Config.register_last_ip_address + current_user.set_last_ip_address(request.remote_ip) end end diff --git a/lib/sorcery/controller/submodules/external.rb b/lib/sorcery/controller/submodules/external.rb index 3c78aca8..bb0b08d8 100644 --- a/lib/sorcery/controller/submodules/external.rb +++ b/lib/sorcery/controller/submodules/external.rb @@ -34,17 +34,17 @@ def external_providers=(providers) @external_providers = providers providers.each do |name| - class_eval <<-E + class_eval <<-RUBY, __FILE__, __LINE__ + 1 def self.#{name} @#{name} ||= Sorcery::Providers.const_get('#{name}'.to_s.capitalize).new end - E + RUBY end end def merge_external_defaults! @defaults.merge!(:@external_providers => [], - :@ca_file => File.join(File.expand_path(File.dirname(__FILE__)), '../../protocols/certs/ca-bundle.crt')) + :@ca_file => File.join(__dir__, '../../protocols/certs/ca-bundle.crt')) end end merge_external_defaults! @@ -57,6 +57,7 @@ module InstanceMethods # save the singleton ProviderClient instance into @provider def sorcery_get_provider(provider_name) return unless Config.external_providers.include?(provider_name.to_sym) + Config.send(provider_name.to_sym) end @@ -65,12 +66,11 @@ def sorcery_get_provider(provider_name) def sorcery_login_url(provider_name, args = {}) @provider = sorcery_get_provider provider_name sorcery_fixup_callback_url @provider - if @provider.respond_to?(:login_url) && @provider.has_callback? - @provider.state = args[:state] - return @provider.login_url(params, session) - else - return nil - end + + return nil unless @provider.respond_to?(:login_url) && @provider.has_callback? + + @provider.state = args[:state] + @provider.login_url(params, session) end # get the user hash from a provider using information from the params and session. @@ -89,6 +89,7 @@ def sorcery_fetch_user_hash(provider_name) # cache them in instance variables. @access_token ||= @provider.process_callback(params, session) # sends request to oauth agent to get the token @user_hash ||= @provider.get_user_hash(@access_token) # uses the token to send another request to the oauth agent requesting user info + nil end # for backwards compatibility @@ -99,14 +100,15 @@ def access_token(*_args) # this method should be somewhere else. It only does something once per application per provider. def sorcery_fixup_callback_url(provider) provider.original_callback_url ||= provider.callback_url - if provider.original_callback_url.present? && provider.original_callback_url[0] == '/' - uri = URI.parse(request.url.gsub(/\?.*$/, '')) - uri.path = '' - uri.query = nil - uri.scheme = 'https' if request.env['HTTP_X_FORWARDED_PROTO'] == 'https' - host = uri.to_s - provider.callback_url = "#{host}#{@provider.original_callback_url}" - end + + return unless provider.original_callback_url.present? && provider.original_callback_url[0] == '/' + + uri = URI.parse(request.url.gsub(/\?.*$/, '')) + uri.path = '' + uri.query = nil + uri.scheme = 'https' if request.env['HTTP_X_FORWARDED_PROTO'] == 'https' + host = uri.to_s + provider.callback_url = "#{host}#{@provider.original_callback_url}" end # sends user to authenticate at the provider's website. @@ -119,26 +121,26 @@ def login_at(provider_name, args = {}) def login_from(provider_name, should_remember = false) sorcery_fetch_user_hash provider_name - if user = user_class.load_from_provider(provider_name, @user_hash[:uid].to_s) - # we found the user. - # clear the session - return_to_url = session[:return_to_url] - reset_sorcery_session - session[:return_to_url] = return_to_url + return unless (user = user_class.load_from_provider(provider_name, @user_hash[:uid].to_s)) - # sign in the user - auto_login(user, should_remember) - after_login!(user) + # we found the user. + # clear the session + return_to_url = session[:return_to_url] + reset_sorcery_session + session[:return_to_url] = return_to_url - # return the user - user - end + # sign in the user + auto_login(user, should_remember) + after_login!(user) + + # return the user + user end # If user is logged, he can add all available providers into his account def add_provider_to_user(provider_name) sorcery_fetch_user_hash provider_name - config = user_class.sorcery_config + # config = user_class.sorcery_config # TODO: Unused, remove? current_user.add_provider_to_user(provider_name.to_s, @user_hash[:uid].to_s) end @@ -182,7 +184,7 @@ def create_and_validate_from(provider_name) # def create_from(provider_name, &block) sorcery_fetch_user_hash provider_name - config = user_class.sorcery_config + # config = user_class.sorcery_config # TODO: Unused, remove? attrs = user_attrs(@provider.user_info_mapping, @user_hash) @user = user_class.create_from_provider(provider_name, @user_hash[:uid], attrs, &block) @@ -191,7 +193,7 @@ def create_from(provider_name, &block) # follows the same patterns as create_from, but builds the user instead of creating def build_from(provider_name, &block) sorcery_fetch_user_hash provider_name - config = user_class.sorcery_config + # config = user_class.sorcery_config # TODO: Unused, remove? attrs = user_attrs(@provider.user_info_mapping, @user_hash) @user = user_class.build_from_provider(attrs, &block) @@ -203,7 +205,7 @@ def user_attrs(user_info_mapping, user_hash) if (varr = v.split('/')).size > 1 attribute_value = begin varr.inject(user_hash[:user_info]) { |hash, value| hash[value] } - rescue + rescue StandardError nil end attribute_value.nil? ? attrs : attrs.merge!(k => attribute_value) diff --git a/lib/sorcery/controller/submodules/http_basic_auth.rb b/lib/sorcery/controller/submodules/http_basic_auth.rb index 672615f3..56bf5346 100644 --- a/lib/sorcery/controller/submodules/http_basic_auth.rb +++ b/lib/sorcery/controller/submodules/http_basic_auth.rb @@ -57,6 +57,7 @@ def realm_name_by_controller while current_controller != ActionController::Base result = Config.controller_to_realm_map[current_controller.controller_name] return result if result + current_controller = current_controller.superclass end nil diff --git a/lib/sorcery/controller/submodules/session_timeout.rb b/lib/sorcery/controller/submodules/session_timeout.rb index 08dfd285..b0cc65b9 100644 --- a/lib/sorcery/controller/submodules/session_timeout.rb +++ b/lib/sorcery/controller/submodules/session_timeout.rb @@ -32,6 +32,7 @@ module InstanceMethods def invalidate_active_sessions! return unless Config.session_timeout_invalidate_active_sessions_enabled return unless current_user.present? + current_user.send(:invalidate_sessions_before=, Time.now.in_time_zone) current_user.save end @@ -64,6 +65,7 @@ def sorcery_session_expired?(time) def sorcery_session_invalidated? return false unless Config.session_timeout_invalidate_active_sessions_enabled return false unless current_user.present? && current_user.try(:invalidate_sessions_before).present? + time = session[:login_time] || session[:last_action_time] || Time.now.in_time_zone time < current_user.invalidate_sessions_before end diff --git a/lib/sorcery/crypto_providers/aes256.rb b/lib/sorcery/crypto_providers/aes256.rb index 616f0f92..81dd2b68 100644 --- a/lib/sorcery/crypto_providers/aes256.rb +++ b/lib/sorcery/crypto_providers/aes256.rb @@ -43,6 +43,7 @@ def decrypt(crypted) def aes raise ArgumentError, "#{name} expects a 32 bytes long key. Please use Sorcery::Model::Config.encryption_key to set it." if @key.nil? || @key == '' + @aes ||= OpenSSL::Cipher.new('AES-256-ECB') end end diff --git a/lib/sorcery/crypto_providers/bcrypt.rb b/lib/sorcery/crypto_providers/bcrypt.rb index 4c1a5655..85413990 100644 --- a/lib/sorcery/crypto_providers/bcrypt.rb +++ b/lib/sorcery/crypto_providers/bcrypt.rb @@ -60,6 +60,7 @@ def encrypt(*tokens) def matches?(hash, *tokens) hash = new_from_hash(hash) return false if hash.nil? || hash == {} + hash == join_tokens(tokens) end @@ -87,7 +88,7 @@ def join_tokens(tokens) def new_from_hash(hash) ::BCrypt::Password.new(hash) rescue ::BCrypt::Errors::InvalidHash - return nil + nil end end end diff --git a/lib/sorcery/model.rb b/lib/sorcery/model.rb index c36fb2fd..a195bdd7 100644 --- a/lib/sorcery/model.rb +++ b/lib/sorcery/model.rb @@ -47,12 +47,15 @@ def include_required_submodules! class_eval do @sorcery_config.submodules = ::Sorcery::Controller::Config.submodules @sorcery_config.submodules.each do |mod| + # TODO: Is there a cleaner way to handle missing submodules? + # rubocop:disable Lint/HandleExceptions begin include Submodules.const_get(mod.to_s.split('_').map(&:capitalize).join) rescue NameError # don't stop on a missing submodule. Needed because some submodules are only defined # in the controller side. end + # rubocop:enable Lint/HandleExceptions end end end @@ -192,9 +195,9 @@ def clear_virtual_password config = sorcery_config send(:"#{config.password_attribute_name}=", nil) - if respond_to?(:"#{config.password_attribute_name}_confirmation=") - send(:"#{config.password_attribute_name}_confirmation=", nil) - end + return unless respond_to?(:"#{config.password_attribute_name}_confirmation=") + + send(:"#{config.password_attribute_name}_confirmation=", nil) end # calls the requested email method on the configured mailer @@ -202,9 +205,9 @@ def clear_virtual_password def generic_send_email(method, mailer) config = sorcery_config mail = config.send(mailer).send(config.send(method), self) - if defined?(ActionMailer) && config.send(mailer).is_a?(Class) && config.send(mailer) < ActionMailer::Base - mail.send(config.email_delivery_method) - end + return unless defined?(ActionMailer) && config.send(mailer).is_a?(Class) && config.send(mailer) < ActionMailer::Base + + mail.send(config.email_delivery_method) end end end diff --git a/lib/sorcery/model/config.rb b/lib/sorcery/model/config.rb index 0bad9332..885c27ee 100644 --- a/lib/sorcery/model/config.rb +++ b/lib/sorcery/model/config.rb @@ -4,8 +4,6 @@ module Sorcery module Model class Config - # change default username attribute, for example, to use :email as the login. - attr_accessor :username_attribute_names # change *virtual* password attribute, the one which is used until an encrypted one is generated. attr_accessor :password_attribute_name # change default email attribute. @@ -38,6 +36,8 @@ class Config # Set token randomness attr_accessor :token_randomness + # change default username attribute, for example, to use :email as the login. See 'username_attribute_names=' below. + attr_reader :username_attribute_names # change default encryption_provider. attr_reader :encryption_provider # use an external encryption class. @@ -96,7 +96,7 @@ def encryption_algorithm=(algo) when :bcrypt then CryptoProviders::BCrypt when :custom then @custom_encryption_provider else raise ArgumentError, "Encryption algorithm supplied, #{algo}, is invalid" - end + end end private diff --git a/lib/sorcery/model/submodules/brute_force_protection.rb b/lib/sorcery/model/submodules/brute_force_protection.rb index 51950e38..18c55074 100644 --- a/lib/sorcery/model/submodules/brute_force_protection.rb +++ b/lib/sorcery/model/submodules/brute_force_protection.rb @@ -14,7 +14,6 @@ def self.included(base) :consecutive_login_retries_amount_limit, # how many failed logins allowed. :login_lock_time_period, # how long the user should be banned. # in seconds. 0 for permanent. - :unlock_token_attribute_name, # Unlock token attribute name :unlock_token_email_method_name, # Mailer method name :unlock_token_mailer_disabled, # When true, dont send unlock token via email @@ -70,9 +69,9 @@ def register_failed_login! sorcery_adapter.increment(config.failed_logins_count_attribute_name) - if send(config.failed_logins_count_attribute_name) >= config.consecutive_login_retries_amount_limit - login_lock! - end + return unless send(config.failed_logins_count_attribute_name) >= config.consecutive_login_retries_amount_limit + + login_lock! end # /!\ @@ -98,9 +97,9 @@ def login_lock! config.unlock_token_attribute_name => TemporaryToken.generate_random_token } sorcery_adapter.update_attributes(attributes) - unless config.unlock_token_mailer_disabled || config.unlock_token_mailer.nil? - send_unlock_token_email! - end + return if config.unlock_token_mailer_disabled || config.unlock_token_mailer.nil? + + send_unlock_token_email! end def login_unlocked? diff --git a/lib/sorcery/model/submodules/external.rb b/lib/sorcery/model/submodules/external.rb index 864bc3ef..7c33d8a0 100644 --- a/lib/sorcery/model/submodules/external.rb +++ b/lib/sorcery/model/submodules/external.rb @@ -40,7 +40,8 @@ module ClassMethods def load_from_provider(provider, uid) config = sorcery_config authentication = config.authentications_class.sorcery_adapter.find_by_oauth_credentials(provider, uid) - user = sorcery_adapter.find_by_id(authentication.send(config.authentications_user_id_attribute_name)) if authentication + # Return user if matching authentication found + sorcery_adapter.find_by_id(authentication.send(config.authentications_user_id_attribute_name)) if authentication end def create_and_validate_from_provider(provider, uid, attrs) diff --git a/lib/sorcery/model/submodules/magic_login.rb b/lib/sorcery/model/submodules/magic_login.rb index b1e534cc..409cbdce 100644 --- a/lib/sorcery/model/submodules/magic_login.rb +++ b/lib/sorcery/model/submodules/magic_login.rb @@ -16,24 +16,18 @@ def self.included(base) :magic_login_token_expires_at_attribute_name, # expires at attribute name. :magic_login_email_sent_at_attribute_name, # when was email sent, used for hammering # protection. - :magic_login_mailer_class, # mailer class. Needed. - :magic_login_mailer_disabled, # when true sorcery will not automatically # email magic login details and allow you to # manually handle how and when email is sent - :magic_login_email_method_name, # magic login email method on your # mailer class. - :magic_login_expiration_period, # how many seconds before the request # expires. nil for never expires. - :magic_login_time_between_emails # hammering protection, how long to wait # before allowing another email to be sent. - end - + base.sorcery_config.instance_eval do @defaults.merge!(:@magic_login_token_attribute_name => :magic_login_token, :@magic_login_token_expires_at_attribute_name => :magic_login_token_expires_at, @@ -43,19 +37,18 @@ def self.included(base) :@magic_login_email_method_name => :magic_login_email, :@magic_login_expiration_period => 15 * 60, :@magic_login_time_between_emails => 5 * 60) - + reset! end - + base.extend(ClassMethods) - + base.sorcery_config.after_config << :validate_mailer_defined base.sorcery_config.after_config << :define_magic_login_fields - + base.send(:include, InstanceMethods) - end - + module ClassMethods # Find user by token, also checks for expiration. # Returns the user if token found and is valid. @@ -64,44 +57,45 @@ def load_from_magic_login_token(token) token_expiration_date_attr = @sorcery_config.magic_login_token_expires_at_attribute_name load_from_token(token, token_attr_name, token_expiration_date_attr) end - + protected - + # This submodule requires the developer to define his own mailer class to be used by it # when magic_login_mailer_disabled is false def validate_mailer_defined - msg = "To use magic_login submodule, you must define a mailer (config.magic_login_mailer_class = YourMailerClass)." - raise ArgumentError, msg if @sorcery_config.magic_login_mailer_class.nil? and @sorcery_config.magic_login_mailer_disabled == false + msg = 'To use magic_login submodule, you must define a mailer (config.magic_login_mailer_class = YourMailerClass).' + raise ArgumentError, msg if @sorcery_config.magic_login_mailer_class.nil? && @sorcery_config.magic_login_mailer_disabled == false end - + def define_magic_login_fields sorcery_adapter.define_field sorcery_config.magic_login_token_attribute_name, String sorcery_adapter.define_field sorcery_config.magic_login_token_expires_at_attribute_name, Time sorcery_adapter.define_field sorcery_config.magic_login_email_sent_at_attribute_name, Time end - end - + module InstanceMethods # generates a reset code with expiration def generate_magic_login_token! config = sorcery_config - attributes = {config.magic_login_token_attribute_name => TemporaryToken.generate_random_token, - config.magic_login_email_sent_at_attribute_name => Time.now.in_time_zone} + attributes = { + config.magic_login_token_attribute_name => TemporaryToken.generate_random_token, + config.magic_login_email_sent_at_attribute_name => Time.now.in_time_zone + } attributes[config.magic_login_token_expires_at_attribute_name] = Time.now.in_time_zone + config.magic_login_expiration_period if config.magic_login_expiration_period - - self.sorcery_adapter.update_attributes(attributes) + + sorcery_adapter.update_attributes(attributes) end - + # generates a magic login code with expiration and sends an email to the user. def deliver_magic_login_instructions! mail = false config = sorcery_config # hammering protection return false if !config.magic_login_time_between_emails.nil? && - self.send(config.magic_login_email_sent_at_attribute_name) && - self.send(config.magic_login_email_sent_at_attribute_name) > config.magic_login_time_between_emails.seconds.ago - + send(config.magic_login_email_sent_at_attribute_name) && + send(config.magic_login_email_sent_at_attribute_name) > config.magic_login_time_between_emails.seconds.ago + self.class.sorcery_adapter.transaction do generate_magic_login_token! unless config.magic_login_mailer_disabled @@ -111,23 +105,22 @@ def deliver_magic_login_instructions! end mail end - + # Clears the token. def clear_magic_login_token! config = sorcery_config - self.sorcery_adapter.update_attributes({ - config.magic_login_token_attribute_name => nil, - config.magic_login_token_expires_at_attribute_name => nil - }) + sorcery_adapter.update_attributes( + config.magic_login_token_attribute_name => nil, + config.magic_login_token_expires_at_attribute_name => nil + ) end - + protected - + def send_magic_login_email! generic_send_email(:magic_login_email_method_name, :magic_login_mailer_class) end end - end end end diff --git a/lib/sorcery/model/submodules/reset_password.rb b/lib/sorcery/model/submodules/reset_password.rb index 1c0a5f7f..48e5f312 100644 --- a/lib/sorcery/model/submodules/reset_password.rb +++ b/lib/sorcery/model/submodules/reset_password.rb @@ -101,6 +101,7 @@ def deliver_reset_password_instructions! config = sorcery_config # hammering protection return false if config.reset_password_time_between_emails.present? && send(config.reset_password_email_sent_at_attribute_name) && send(config.reset_password_email_sent_at_attribute_name) > config.reset_password_time_between_emails.seconds.ago.utc + self.class.sorcery_adapter.transaction do generate_reset_password_token! mail = send_reset_password_email! unless config.reset_password_mailer_disabled @@ -112,7 +113,7 @@ def deliver_reset_password_instructions! # For example, access_count_to_reset_password_page attribute is over 1, which # means the user doesn't have a right to access. def increment_password_reset_page_access_counter - sorcery_adapter.increment(self.sorcery_config.reset_password_page_access_count_attribute_name) + sorcery_adapter.increment(sorcery_config.reset_password_page_access_count_attribute_name) end # Reset access_count_to_reset_password_page attribute into 0. diff --git a/lib/sorcery/protocols/oauth.rb b/lib/sorcery/protocols/oauth.rb index 448602cf..bb59d4d4 100644 --- a/lib/sorcery/protocols/oauth.rb +++ b/lib/sorcery/protocols/oauth.rb @@ -9,6 +9,7 @@ def oauth_version def get_request_token(token = nil, secret = nil) return ::OAuth::RequestToken.new(get_consumer, token, secret) if token && secret + get_consumer.get_request_token(oauth_callback: @callback_url) end diff --git a/lib/sorcery/providers/heroku.rb b/lib/sorcery/providers/heroku.rb index cd420d56..ac6427bd 100644 --- a/lib/sorcery/providers/heroku.rb +++ b/lib/sorcery/providers/heroku.rb @@ -45,6 +45,7 @@ def login_url(_params, _session) # tries to login the user from access token def process_callback(params, _session) raise 'Invalid state. Potential Cross Site Forgery' if params[:state] != state + args = {}.tap do |a| a[:code] = params[:code] if params[:code] end diff --git a/lib/sorcery/providers/instagram.rb b/lib/sorcery/providers/instagram.rb index cb6d42d1..a01e35e3 100644 --- a/lib/sorcery/providers/instagram.rb +++ b/lib/sorcery/providers/instagram.rb @@ -1,21 +1,17 @@ module Sorcery module Providers # This class adds support for OAuth with Instagram.com. - class Instagram < Base - include Protocols::Oauth2 - attr_accessor :access_permissions, :token_url, :authorization_path, :user_info_path, :scope, :user_info_fields - def initialize super - @site = 'https://api.instagram.com' + @site = 'https://api.instagram.com' @token_url = '/oauth/access_token' @authorization_path = '/oauth/authorize/' @user_info_path = '/v1/users/self' @@ -31,11 +27,10 @@ def login_url(_params, _session) authorize_url(token_url: @token_url) end - # overrides oauth2#authorize_url to allow customized scope. def authorize_url(opts = {}) @scope = access_permissions.present? ? access_permissions.join(' ') : scope - super(opts.merge(:token_url => @token_url)) + super(opts.merge(token_url: @token_url)) end # pass oauth2 param `code` provided by instgrm server @@ -43,11 +38,14 @@ def process_callback(params, _session) args = {}.tap do |a| a[:code] = params[:code] if params[:code] end - get_access_token(args, token_url: @token_url, - client_id: @key, client_secret: @secret) + get_access_token( + args, + token_url: @token_url, + client_id: @key, + client_secret: @secret + ) end - # see `user_info_mapping` in config/initializer, # given `user_info_mapping` to specify # {:db_attribute_name => 'instagram_attr_name'} @@ -58,21 +56,18 @@ def process_callback(params, _session) # testing strategy relies on querying user_info_path def get_user_hash(access_token) call_api_params = { - :access_token => access_token.token, - :client_id => access_token[:client_id] + access_token: access_token.token, + client_id: access_token[:client_id] } response = access_token.get( - "#{user_info_path}?#{call_api_params.to_param}" + "#{user_info_path}?#{call_api_params.to_param}" ) - _user_attrs = Hash.new - _user_attrs[:user_info] = JSON.parse(response.body)['data'] - _user_attrs[:uid] = _user_attrs[:user_info]['id'] - _user_attrs + user_attrs = {} + user_attrs[:user_info] = JSON.parse(response.body)['data'] + user_attrs[:uid] = user_attrs[:user_info]['id'] + user_attrs end - end - end - end diff --git a/lib/sorcery/providers/linkedin.rb b/lib/sorcery/providers/linkedin.rb index 239ad877..7404a9fe 100644 --- a/lib/sorcery/providers/linkedin.rb +++ b/lib/sorcery/providers/linkedin.rb @@ -31,7 +31,7 @@ def get_consumer def get_user_hash(access_token) # Always include id for provider uid and prevent accidental duplication via setting `user_info_field = ['id']` (needed in Sorcery 0.9.1) - info_fields = user_info_fields ? user_info_fields.reject{|n| n == 'id'} : [] + info_fields = user_info_fields ? user_info_fields.reject { |n| n == 'id' } : [] fields = info_fields.any? ? 'id,' + info_fields.join(',') : 'id' response = access_token.get("#{@user_info_path}:(#{fields})", 'x-li-format' => 'json') diff --git a/lib/sorcery/providers/vk.rb b/lib/sorcery/providers/vk.rb index 82edf6f4..ebbaf636 100644 --- a/lib/sorcery/providers/vk.rb +++ b/lib/sorcery/providers/vk.rb @@ -33,7 +33,7 @@ def get_user_hash(access_token) } response = access_token.get(user_info_url, params: params) - if user_hash[:user_info] = JSON.parse(response.body) + if (user_hash[:user_info] = JSON.parse(response.body)) user_hash[:user_info] = user_hash[:user_info]['response'][0] user_hash[:user_info]['full_name'] = [user_hash[:user_info]['first_name'], user_hash[:user_info]['last_name']].join(' ') diff --git a/lib/sorcery/providers/wechat.rb b/lib/sorcery/providers/wechat.rb index 329246d0..fb023429 100644 --- a/lib/sorcery/providers/wechat.rb +++ b/lib/sorcery/providers/wechat.rb @@ -37,10 +37,13 @@ def authorize_url(options = {}) end def get_user_hash(access_token) - response = access_token.get(user_info_path, params: { - access_token: access_token.token, - openid: access_token.params['openid'], - }) + response = access_token.get( + user_info_path, + params: { + access_token: access_token.token, + openid: access_token.params['openid'] + } + ) {}.tap do |h| h[:user_info] = JSON.parse(response.body) @@ -70,10 +73,9 @@ def process_callback(params, _session) args, token_url: token_url, mode: mode, - param_name: param_name, + param_name: param_name ) end end end end - diff --git a/lib/sorcery/test_helpers/internal.rb b/lib/sorcery/test_helpers/internal.rb index 6f603c27..1201ac2b 100644 --- a/lib/sorcery/test_helpers/internal.rb +++ b/lib/sorcery/test_helpers/internal.rb @@ -17,7 +17,7 @@ def cost # a patch to fix a bug in testing that happens when you 'destroy' a session twice. # After the first destroy, the session is an ordinary hash, and then when destroy # is called again there's an exception. - class ::Hash + class ::Hash # rubocop:disable Style/ClassAndModuleChildren def destroy clear end @@ -69,9 +69,10 @@ def update_model(&block) def reload_user_class User && Object.send(:remove_const, 'User') load 'user.rb' - if User.respond_to?(:reset_column_information) - User.reset_column_information - end + + return unless User.respond_to?(:reset_column_information) + + User.reset_column_information end end end diff --git a/lib/sorcery/test_helpers/internal/rails.rb b/lib/sorcery/test_helpers/internal/rails.rb index 28e4a45d..a9eb4365 100644 --- a/lib/sorcery/test_helpers/internal/rails.rb +++ b/lib/sorcery/test_helpers/internal/rails.rb @@ -4,11 +4,11 @@ module Internal module Rails include ::Sorcery::TestHelpers::Rails::Controller - SUBMODULES_AUTO_ADDED_CONTROLLER_FILTERS = [ - :register_last_activity_time_to_db, - :deny_banned_user, - :validate_session - ] + SUBMODULES_AUTO_ADDED_CONTROLLER_FILTERS = %i[ + register_last_activity_time_to_db + deny_banned_user + validate_session + ].freeze def sorcery_reload!(submodules = [], options = {}) reload_user_class @@ -40,11 +40,11 @@ def sorcery_reload!(submodules = [], options = {}) end end User.authenticates_with_sorcery! - if defined?(DataMapper) && User.ancestors.include?(DataMapper::Resource) - DataMapper.auto_migrate! - User.finalize - Authentication.finalize - end + return unless defined?(DataMapper) && User.ancestors.include?(DataMapper::Resource) + + DataMapper.auto_migrate! + User.finalize + Authentication.finalize end def sorcery_controller_property_set(property, value) @@ -64,7 +64,7 @@ def clear_user_without_logout end if ::Rails.version < '5.0.0' - %w(get post put).each do |method| + %w[get post put].each do |method| define_method(method) do |action, options = {}| super action, options[:params] || {}, options[:session] end diff --git a/lib/sorcery/version.rb b/lib/sorcery/version.rb index 1c8791d0..cf14d722 100644 --- a/lib/sorcery/version.rb +++ b/lib/sorcery/version.rb @@ -1,3 +1,3 @@ module Sorcery - VERSION = '0.12.0' + VERSION = '0.12.0'.freeze end diff --git a/sorcery.gemspec b/sorcery.gemspec index 4668a2a3..a62c40b0 100644 --- a/sorcery.gemspec +++ b/sorcery.gemspec @@ -1,34 +1,50 @@ -lib = File.expand_path('../lib', __FILE__) +lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'sorcery/version' +# rubocop:disable Metrics/BlockLength Gem::Specification.new do |s| s.name = 'sorcery' s.version = Sorcery::VERSION - s.authors = ['Noam Ben Ari', 'Kir Shatrov', 'Grzegorz Witek', 'Chase Gilliam'] - s.email = 'chase.gilliam@gmail.com' + s.authors = [ + 'Noam Ben Ari', + 'Kir Shatrov', + 'Grzegorz Witek', + 'Chase Gilliam', + 'Josh Buker' + ] + s.email = [ + 'chase.gilliam@gmail.com', + 'contact@joshbuker.com' + ] + + # TODO: Cleanup formatting. + # rubocop:disable Metrics/LineLength s.description = 'Provides common authentication needs such as signing in/out, activating by email and resetting password.' s.summary = 'Magical authentication for Rails applications' s.homepage = 'https://github.com/Sorcery/sorcery' s.post_install_message = "As of version 1.0 oauth/oauth2 won't be automatically bundled so you may need to add those dependencies to your Gemfile.\n" s.post_install_message += 'You may need oauth2 if you use external providers such as any of these: https://github.com/Sorcery/sorcery/tree/master/lib/sorcery/providers' + # rubocop:enable Metrics/LineLength s.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR) s.require_paths = ['lib'] s.licenses = ['MIT'] - s.required_ruby_version = '>= 2.2.2' + s.required_ruby_version = '>= 2.2.9' + s.add_dependency 'bcrypt', '~> 3.1' s.add_dependency 'oauth', '~> 0.4', '>= 0.4.4' s.add_dependency 'oauth2', '~> 1.0', '>= 0.8.0' - s.add_dependency 'bcrypt', '~> 3.1' - s.add_development_dependency 'yard', '~> 0.9.0', '>= 0.9.12' - s.add_development_dependency 'timecop' - s.add_development_dependency 'simplecov', '>= 0.3.8' + s.add_development_dependency 'byebug', '~> 10.0.0' s.add_development_dependency 'rspec-rails', '~> 3.7.0' + s.add_development_dependency 'rubocop' + s.add_development_dependency 'simplecov', '>= 0.3.8' s.add_development_dependency 'test-unit', '~> 3.2.0' - s.add_development_dependency 'byebug', '~> 10.0.0' + s.add_development_dependency 'timecop' s.add_development_dependency 'webmock', '~> 3.3.0' + s.add_development_dependency 'yard', '~> 0.9.0', '>= 0.9.12' end +# rubocop:enable Metrics/BlockLength diff --git a/spec/active_record/user_magic_login_spec.rb b/spec/active_record/user_magic_login_spec.rb index a561e739..d40f8bdb 100644 --- a/spec/active_record/user_magic_login_spec.rb +++ b/spec/active_record/user_magic_login_spec.rb @@ -6,10 +6,10 @@ MigrationHelper.migrate("#{Rails.root}/db/migrate/magic_login") User.reset_column_information end - + after(:all) do MigrationHelper.rollback("#{Rails.root}/db/migrate/magic_login") end - + it_behaves_like 'magic_login_model' end diff --git a/spec/controllers/controller_oauth2_spec.rb b/spec/controllers/controller_oauth2_spec.rb index 4702e6b8..b4b3d792 100644 --- a/spec/controllers/controller_oauth2_spec.rb +++ b/spec/controllers/controller_oauth2_spec.rb @@ -155,7 +155,7 @@ expect(flash[:notice]).to eq 'Success!' end - [:github, :google, :liveid, :vk, :salesforce, :paypal, :slack, :wechat, :microsoft, :instagram].each do |provider| + %i[github google liveid vk salesforce paypal slack wechat microsoft instagram].each do |provider| describe "with #{provider}" do it 'login_at redirects correctly' do get :"login_at_test_#{provider}" @@ -200,43 +200,58 @@ describe 'OAuth with User Activation features' do before(:all) do - sorcery_reload!([:user_activation,:external], :user_activation_mailer => ::SorceryMailer) - sorcery_controller_property_set(:external_providers, [:facebook, :github, :google, :liveid, :vk, :salesforce, :paypal, :slack, :wechat, :microsoft, :instagram]) + sorcery_reload!(%i[user_activation external], user_activation_mailer: ::SorceryMailer) + sorcery_controller_property_set( + :external_providers, + %i[ + facebook + github + google + liveid + vk + salesforce + paypal + slack + wechat + microsoft + instagram + ] + ) # TODO: refactor - sorcery_controller_external_property_set(:facebook, :key, "eYVNBjBDi33aa9GkA3w") - sorcery_controller_external_property_set(:facebook, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") - sorcery_controller_external_property_set(:facebook, :callback_url, "http://blabla.com") - sorcery_controller_external_property_set(:github, :key, "eYVNBjBDi33aa9GkA3w") - sorcery_controller_external_property_set(:github, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") - sorcery_controller_external_property_set(:github, :callback_url, "http://blabla.com") - sorcery_controller_external_property_set(:google, :key, "eYVNBjBDi33aa9GkA3w") - sorcery_controller_external_property_set(:google, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") - sorcery_controller_external_property_set(:google, :callback_url, "http://blabla.com") - sorcery_controller_external_property_set(:liveid, :key, "eYVNBjBDi33aa9GkA3w") - sorcery_controller_external_property_set(:liveid, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") - sorcery_controller_external_property_set(:liveid, :callback_url, "http://blabla.com") - sorcery_controller_external_property_set(:vk, :key, "eYVNBjBDi33aa9GkA3w") - sorcery_controller_external_property_set(:vk, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") - sorcery_controller_external_property_set(:vk, :callback_url, "http://blabla.com") - sorcery_controller_external_property_set(:salesforce, :key, "eYVNBjBDi33aa9GkA3w") - sorcery_controller_external_property_set(:salesforce, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") - sorcery_controller_external_property_set(:salesforce, :callback_url, "http://blabla.com") - sorcery_controller_external_property_set(:paypal, :key, "eYVNBjBDi33aa9GkA3w") - sorcery_controller_external_property_set(:paypal, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") - sorcery_controller_external_property_set(:paypal, :callback_url, "http://blabla.com") - sorcery_controller_external_property_set(:slack, :key, "eYVNBjBDi33aa9GkA3w") - sorcery_controller_external_property_set(:slack, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") - sorcery_controller_external_property_set(:slack, :callback_url, "http://blabla.com") - sorcery_controller_external_property_set(:wechat, :key, "eYVNBjBDi33aa9GkA3w") - sorcery_controller_external_property_set(:wechat, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") - sorcery_controller_external_property_set(:wechat, :callback_url, "http://blabla.com") - sorcery_controller_external_property_set(:microsoft, :key, "eYVNBjBDi33aa9GkA3w") - sorcery_controller_external_property_set(:microsoft, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") - sorcery_controller_external_property_set(:microsoft, :callback_url, "http://blabla.com") - sorcery_controller_external_property_set(:instagram, :key, "eYVNBjBDi33aa9GkA3w") - sorcery_controller_external_property_set(:instagram, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") - sorcery_controller_external_property_set(:instagram, :callback_url, "http://blabla.com") + sorcery_controller_external_property_set(:facebook, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:facebook, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:facebook, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:github, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:github, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:github, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:google, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:google, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:google, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:liveid, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:liveid, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:liveid, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:vk, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:vk, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:vk, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:salesforce, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:salesforce, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:salesforce, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:paypal, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:paypal, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:paypal, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:slack, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:slack, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:slack, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:wechat, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:wechat, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:wechat, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:microsoft, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:microsoft, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:microsoft, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:instagram, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:instagram, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:instagram, :callback_url, 'http://blabla.com') end after(:each) do @@ -259,7 +274,7 @@ expect(ActionMailer::Base.deliveries.size).to eq old_size end - [:github, :google, :liveid, :vk, :salesforce, :paypal, :wechat, :microsoft, :instagram].each do |provider| + %i[github google liveid vk salesforce paypal wechat microsoft instagram].each do |provider| it "does not send activation email to external users (#{provider})" do old_size = ActionMailer::Base.deliveries.size create_new_external_user provider @@ -271,6 +286,7 @@ create_new_external_user provider old_size = ActionMailer::Base.deliveries.size @user.activate! + expect(ActionMailer::Base.deliveries.size).to eq old_size end end end @@ -279,10 +295,10 @@ let(:user) { double('user', id: 42) } before(:all) do - sorcery_reload!([:activity_logging, :external]) + sorcery_reload!(%i[activity_logging external]) end - %w(facebook github google liveid vk salesforce slack).each do |provider| + %w[facebook github google liveid vk salesforce slack].each do |provider| context "when #{provider}" do before(:each) do sorcery_controller_property_set(:register_login_time, true) @@ -316,12 +332,12 @@ describe 'OAuth with session timeout features' do before(:all) do - sorcery_reload!([:session_timeout, :external]) + sorcery_reload!(%i[session_timeout external]) end let(:user) { double('user', id: 42) } - %w(facebook github google liveid vk salesforce slack).each do |provider| + %w[facebook github google liveid vk salesforce slack].each do |provider| context "when #{provider}" do before(:each) do sorcery_model_property_set(:authentications_class, Authentication) @@ -359,52 +375,76 @@ def stub_all_oauth2_requests! access_token = double(OAuth2::AccessToken) allow(access_token).to receive(:token_param=) # Needed for Instagram - allow(access_token).to receive(:[]).with(:client_id){"eYVNBjBDi33aa9GkA3w"} + allow(access_token).to receive(:[]).with(:client_id) { 'eYVNBjBDi33aa9GkA3w' } response = double(OAuth2::Response) allow(response).to receive(:body) { - { - 'id' => '123', - 'user_id' => '123', # Needed for Salesforce - 'name' => 'Noam Ben Ari', - 'first_name' => 'Noam', - 'last_name' => 'Ben Ari', - 'link' => 'http://www.facebook.com/nbenari1', - 'hometown' => { 'id' => '110619208966868', 'name' => 'Haifa, Israel' }, - 'location' => { 'id' => '106906559341067', 'name' => 'Pardes Hanah, Hefa, Israel' }, - 'bio' => "I'm a new daddy, and enjoying it!", - 'gender' => 'male', - 'email' => 'nbenari@gmail.com', - 'timezone' => 2, - 'locale' => 'en_US', - 'languages' => [{ 'id' => '108405449189952', 'name' => 'Hebrew' }, { 'id' => '106059522759137', 'name' => 'English' }, { 'id' => '112624162082677', 'name' => 'Russian' }], - 'verified' => true, - 'updated_time' => '2011-02-16T20:59:38+0000', - # response for VK auth - 'response' => [ - { - 'uid' => '123', - 'first_name' => 'Noam', - 'last_name' => 'Ben Ari' - } - ], - 'user' => { - 'name' => 'Sonny Whether', - 'id' => '123', - 'email' => 'bobby@example.com' - }, - # response for wechat auth - 'unionid' => '123', - # response for instagram - 'data' => { - 'username' => 'pnmahoney', - 'bio' => 'turn WHAT down?', - 'website' => '', - 'profile_picture' => 'http://photos-d.ak.instagram.com/hphotos-ak-xpa1/10454121_417985815007395_867850883_a.jpg', - 'full_name' => 'Patrick Mahoney', - 'counts' => {'media' => 2, 'followed_by' => 100, 'follows' => 71}, - 'id'=>'123' - } - }.to_json } + { + 'id' => '123', + 'user_id' => '123', # Needed for Salesforce + 'name' => 'Noam Ben Ari', + 'first_name' => 'Noam', + 'last_name' => 'Ben Ari', + 'link' => 'http://www.facebook.com/nbenari1', + 'hometown' => { + 'id' => '110619208966868', + 'name' => 'Haifa, Israel' + }, + 'location' => { + 'id' => '106906559341067', + 'name' => 'Pardes Hanah, Hefa, Israel' + }, + 'bio' => "I'm a new daddy, and enjoying it!", + 'gender' => 'male', + 'email' => 'nbenari@gmail.com', + 'timezone' => 2, + 'locale' => 'en_US', + 'languages' => [ + { + 'id' => '108405449189952', + 'name' => 'Hebrew' + }, + { + 'id' => '106059522759137', + 'name' => 'English' + }, + { + 'id' => '112624162082677', + 'name' => 'Russian' + } + ], + 'verified' => true, + 'updated_time' => '2011-02-16T20:59:38+0000', + # response for VK auth + 'response' => [ + { + 'uid' => '123', + 'first_name' => 'Noam', + 'last_name' => 'Ben Ari' + } + ], + 'user' => { + 'name' => 'Sonny Whether', + 'id' => '123', + 'email' => 'bobby@example.com' + }, + # response for wechat auth + 'unionid' => '123', + # response for instagram + 'data' => { + 'username' => 'pnmahoney', + 'bio' => 'turn WHAT down?', + 'website' => '', + 'profile_picture' => 'http://photos-d.ak.instagram.com/hphotos-ak-xpa1/10454121_417985815007395_867850883_a.jpg', + 'full_name' => 'Patrick Mahoney', + 'counts' => { + 'media' => 2, + 'followed_by' => 100, + 'follows' => 71 + }, + 'id' => '123' + } + }.to_json + } allow(access_token).to receive(:get) { response } allow(access_token).to receive(:token) { '187041a618229fdaf16613e96e1caabc1e86e46bbfad228de41520e63fe45873684c365a14417289599f3' } # access_token params for VK auth @@ -413,40 +453,55 @@ def stub_all_oauth2_requests! end def set_external_property - sorcery_controller_property_set(:external_providers, [:facebook, :github, :google, :liveid, :vk, :salesforce, :paypal, :slack, :wechat, :microsoft, :instagram]) - sorcery_controller_external_property_set(:facebook, :key, "eYVNBjBDi33aa9GkA3w") - sorcery_controller_external_property_set(:facebook, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") - sorcery_controller_external_property_set(:facebook, :callback_url, "http://blabla.com") - sorcery_controller_external_property_set(:github, :key, "eYVNBjBDi33aa9GkA3w") - sorcery_controller_external_property_set(:github, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") - sorcery_controller_external_property_set(:github, :callback_url, "http://blabla.com") - sorcery_controller_external_property_set(:google, :key, "eYVNBjBDi33aa9GkA3w") - sorcery_controller_external_property_set(:google, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") - sorcery_controller_external_property_set(:google, :callback_url, "http://blabla.com") - sorcery_controller_external_property_set(:liveid, :key, "eYVNBjBDi33aa9GkA3w") - sorcery_controller_external_property_set(:liveid, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") - sorcery_controller_external_property_set(:liveid, :callback_url, "http://blabla.com") - sorcery_controller_external_property_set(:vk, :key, "eYVNBjBDi33aa9GkA3w") - sorcery_controller_external_property_set(:vk, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") - sorcery_controller_external_property_set(:vk, :callback_url, "http://blabla.com") - sorcery_controller_external_property_set(:salesforce, :key, "eYVNBjBDi33aa9GkA3w") - sorcery_controller_external_property_set(:salesforce, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") - sorcery_controller_external_property_set(:salesforce, :callback_url, "http://blabla.com") - sorcery_controller_external_property_set(:paypal, :key, "eYVNBjBDi33aa9GkA3w") - sorcery_controller_external_property_set(:paypal, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") - sorcery_controller_external_property_set(:paypal, :callback_url, "http://blabla.com") - sorcery_controller_external_property_set(:slack, :key, "eYVNBjBDi33aa9GkA3w") - sorcery_controller_external_property_set(:slack, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") - sorcery_controller_external_property_set(:slack, :callback_url, "http://blabla.com") - sorcery_controller_external_property_set(:wechat, :key, "eYVNBjBDi33aa9GkA3w") - sorcery_controller_external_property_set(:wechat, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") - sorcery_controller_external_property_set(:wechat, :callback_url, "http://blabla.com") - sorcery_controller_external_property_set(:microsoft, :key, "eYVNBjBDi33aa9GkA3w") - sorcery_controller_external_property_set(:microsoft, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") - sorcery_controller_external_property_set(:microsoft, :callback_url, "http://blabla.com") - sorcery_controller_external_property_set(:instagram, :key, "eYVNBjBDi33aa9GkA3w") - sorcery_controller_external_property_set(:instagram, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") - sorcery_controller_external_property_set(:instagram, :callback_url, "http://blabla.com") + sorcery_controller_property_set( + :external_providers, + %i[ + facebook + github + google + liveid + vk + salesforce + paypal + slack + wechat + microsoft + instagram + ] + ) + sorcery_controller_external_property_set(:facebook, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:facebook, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:facebook, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:github, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:github, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:github, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:google, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:google, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:google, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:liveid, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:liveid, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:liveid, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:vk, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:vk, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:vk, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:salesforce, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:salesforce, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:salesforce, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:paypal, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:paypal, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:paypal, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:slack, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:slack, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:slack, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:wechat, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:wechat, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:wechat, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:microsoft, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:microsoft, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:microsoft, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:instagram, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:instagram, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:instagram, :callback_url, 'http://blabla.com') end def provider_url(provider) diff --git a/spec/controllers/controller_oauth_spec.rb b/spec/controllers/controller_oauth_spec.rb index 13d934d2..31086a0c 100644 --- a/spec/controllers/controller_oauth_spec.rb +++ b/spec/controllers/controller_oauth_spec.rb @@ -20,7 +20,7 @@ def stub_all_oauth_requests! 'in_reply_to_status_id_str' => nil, 'created_at' => 'Sun Mar 06 23:01:12 +0000 2011', 'contributors' => nil, 'place' => nil, 'retweeted' => false, 'in_reply_to_status_id' => nil, 'in_reply_to_user_id_str' => nil, 'coordinates' => nil, 'retweet_count' => 0, - 'id' => 44533012284706816, 'id_str' => '44533012284706816' + 'id' => 44_533_012_284_706_816, 'id_str' => '44533012284706816' }, 'show_all_inline_media' => false, 'geo_enabled' => true, 'profile_sidebar_border_color' => 'a8c7f7', 'url' => nil, 'followers_count' => 10, @@ -32,7 +32,7 @@ def stub_all_oauth_requests! 'is_translator' => false, 'contributors_enabled' => false, 'protected' => false, 'follow_request_sent' => false, 'time_zone' => 'Greenland', 'profile_text_color' => '333333', 'name' => 'Noam Ben Ari', 'friends_count' => 10, 'profile_sidebar_fill_color' => 'C0DFEC', - 'id' => 123, 'id_str' => '91434812', 'profile_background_tile' => false, 'utc_offset' => -10800 + 'id' => 123, 'id_str' => '91434812', 'profile_background_tile' => false, 'utc_offset' => -10_800 }.to_json session[:request_token] = req_token.token @@ -50,7 +50,7 @@ def stub_all_oauth_requests! before(:all) do sorcery_reload!([:external]) - sorcery_controller_property_set(:external_providers, [:twitter, :jira]) + sorcery_controller_property_set(:external_providers, %i[twitter jira]) sorcery_controller_external_property_set(:twitter, :key, 'eYVNBjBDi33aa9GkA3w') sorcery_controller_external_property_set(:twitter, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') sorcery_controller_external_property_set(:twitter, :callback_url, 'http://blabla.com') @@ -179,7 +179,7 @@ def stub_all_oauth_requests! describe SorceryController, 'OAuth with user activation features' do before(:all) do - sorcery_reload!([:activity_logging, :external]) + sorcery_reload!(%i[activity_logging external]) end context 'when twitter' do @@ -219,7 +219,7 @@ def stub_all_oauth_requests! User.reset_column_information end - sorcery_reload!([:session_timeout, :external]) + sorcery_reload!(%i[session_timeout external]) end after(:all) do diff --git a/spec/controllers/controller_remember_me_spec.rb b/spec/controllers/controller_remember_me_spec.rb index 11c331cb..5358bfcb 100644 --- a/spec/controllers/controller_remember_me_spec.rb +++ b/spec/controllers/controller_remember_me_spec.rb @@ -9,10 +9,11 @@ sorcery_reload!([:remember_me]) end - after(:each) do - session = nil - cookies = nil - end + # TODO: Unused, remove? + # after(:each) do + # session = nil + # cookies = nil + # end before(:each) do allow(user).to receive(:remember_me_token) @@ -31,16 +32,18 @@ end it 'clears cookie on forget_me!' do - cookies['remember_me_token'] == { value: 'asd54234dsfsd43534', expires: 3600 } + cookies['remember_me_token'] = { value: 'asd54234dsfsd43534', expires: 3600 } get :test_logout + pending 'Test previously broken, functionality might not be working here.' expect(cookies['remember_me_token']).to be_nil end it 'clears cookie on force_forget_me!' do - cookies['remember_me_token'] == { value: 'asd54234dsfsd43534', expires: 3600 } + cookies['remember_me_token'] = { value: 'asd54234dsfsd43534', expires: 3600 } get :test_logout_with_force_forget_me + pending 'Test previously broken, functionality might not be working here.' expect(cookies['remember_me_token']).to be_nil end diff --git a/spec/controllers/controller_session_timeout_spec.rb b/spec/controllers/controller_session_timeout_spec.rb index 2a3495ce..43e565e2 100644 --- a/spec/controllers/controller_session_timeout_spec.rb +++ b/spec/controllers/controller_session_timeout_spec.rb @@ -45,7 +45,7 @@ get :test_should_be_logged_in expect(session[:user_id]).not_to be_nil - expect(response).to be_a_success + expect(response).to be_successful end it 'does not reset the session if it was not created before invalidate_sessions_before' do @@ -56,7 +56,7 @@ get :test_should_be_logged_in expect(session[:user_id]).not_to be_nil - expect(response).to be_a_success + expect(response).to be_successful end it 'resets the session if the session was created before invalidate_sessions_before' do @@ -74,12 +74,12 @@ # precondition that the user is logged in login_user user get :test_should_be_logged_in - expect(response).to be_a_success + expect(response).to be_successful allow(user).to receive(:send) { |_method, value| allow(user).to receive(:invalidate_sessions_before) { value } } allow(user).to receive(:save) get :test_invalidate_active_session - expect(response).to be_a_success + expect(response).to be_successful get :test_should_be_logged_in expect(session[:user_id]).to be_nil @@ -91,13 +91,13 @@ # precondition that the user is logged in login_user user get :test_should_be_logged_in - expect(response).to be_a_success + expect(response).to be_successful allow(user).to receive(:send) { |_method, value| allow(user).to receive(:invalidate_sessions_before) { value } } allow(user).to receive(:save) # Call to invalidate get :test_invalidate_active_session - expect(response).to be_a_success + expect(response).to be_successful # Check that existing sessions were logged out get :test_should_be_logged_in @@ -107,12 +107,12 @@ # Check that new session is allowed to login login_user user get :test_should_be_logged_in - expect(response).to be_a_success + expect(response).to be_successful expect(session[:user_id]).not_to be_nil # Check an additional request to make sure not logged out on next request get :test_should_be_logged_in - expect(response).to be_a_success + expect(response).to be_successful expect(session[:user_id]).not_to be_nil end end @@ -157,7 +157,7 @@ end end - it "registers login time on remember_me callback" do + it 'registers login time on remember_me callback' do expect(subject).to receive(:register_login_time).with(user) subject.send(:after_remember_me!, user) diff --git a/spec/controllers/controller_spec.rb b/spec/controllers/controller_spec.rb index 9d564e9c..83e00a21 100644 --- a/spec/controllers/controller_spec.rb +++ b/spec/controllers/controller_spec.rb @@ -143,7 +143,7 @@ end it 'require_login before_action does not save the url that the user originally wanted upon all non-get http methods' do - [:post, :put, :delete].each do |m| + %i[post put delete].each do |m| send(m, :some_action) expect(session[:return_to_url]).to be_nil diff --git a/spec/providers/vk_spec.rb b/spec/providers/vk_spec.rb index 0a885a16..2a918d48 100644 --- a/spec/providers/vk_spec.rb +++ b/spec/providers/vk_spec.rb @@ -11,25 +11,27 @@ before(:all) do sorcery_reload!([:external]) sorcery_controller_property_set(:external_providers, [:vk]) - sorcery_controller_external_property_set(:vk, :key, "KEY") - sorcery_controller_external_property_set(:vk, :secret, "SECRET") + sorcery_controller_external_property_set(:vk, :key, 'KEY') + sorcery_controller_external_property_set(:vk, :secret, 'SECRET') end def stub_vk_authorize - stub_request(:post, /https\:\/\/oauth\.vk\.com\/access_token/) - .to_return( - status: 200, - body: '{"access_token":"TOKEN","expires_in":86329,"user_id":1}', - headers: {'content-type' => 'application/json'}) + stub_request(:post, %r{https\:\/\/oauth\.vk\.com\/access_token}).to_return( + status: 200, + body: '{"access_token":"TOKEN","expires_in":86329,"user_id":1}', + headers: { 'content-type' => 'application/json' } + ) end - context "getting user info hash" do - it "should provide VK API version" do + context 'getting user info hash' do + it 'should provide VK API version' do stub_vk_authorize sorcery_controller_external_property_set(:vk, :api_version, '5.71') - get_user = stub_request(:get, "https://api.vk.com/method/getProfiles?access_token=TOKEN&fields=&scope=email&uids=1&v=5.71") - .to_return(body: '{"response":[{"id":1}]}') + get_user = stub_request( + :get, + 'https://api.vk.com/method/getProfiles?access_token=TOKEN&fields=&scope=email&uids=1&v=5.71' + ).to_return(body: '{"response":[{"id":1}]}') token = provider.process_callback({ code: 'CODE' }, nil) provider.get_user_hash(token) @@ -37,5 +39,4 @@ def stub_vk_authorize expect(get_user).to have_been_requested end end - end diff --git a/spec/rails_app/app/controllers/sorcery_controller.rb b/spec/rails_app/app/controllers/sorcery_controller.rb index 938e85be..1fdf93a6 100644 --- a/spec/rails_app/app/controllers/sorcery_controller.rb +++ b/spec/rails_app/app/controllers/sorcery_controller.rb @@ -4,7 +4,12 @@ class SorceryController < ActionController::Base protect_from_forgery before_action :require_login_from_http_basic, only: [:test_http_basic_auth] - before_action :require_login, only: [:test_logout, :test_logout_with_force_forget_me, :test_should_be_logged_in, :some_action] + before_action :require_login, only: %i[ + test_logout + test_logout_with_force_forget_me + test_should_be_logged_in + some_action + ] def index; end @@ -146,7 +151,7 @@ def login_at_test_instagram end def test_login_from_twitter - if @user = login_from(:twitter) + if (@user = login_from(:twitter)) redirect_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -156,7 +161,7 @@ def test_login_from_twitter alias test_login_from test_login_from_twitter def test_login_from_facebook - if @user = login_from(:facebook) + if (@user = login_from(:facebook)) redirect_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -164,7 +169,7 @@ def test_login_from_facebook end def test_login_from_github - if @user = login_from(:github) + if (@user = login_from(:github)) redirect_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -172,7 +177,7 @@ def test_login_from_github end def test_login_from_paypal - if @user = login_from(:paypal) + if (@user = login_from(:paypal)) redirect_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -180,7 +185,7 @@ def test_login_from_paypal end def test_login_from_wechat - if @user = login_from(:wechat) + if (@user = login_from(:wechat)) redirect_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -188,7 +193,7 @@ def test_login_from_wechat end def test_login_from_microsoft - if @user = login_from(:microsoft) + if (@user = login_from(:microsoft)) redirect_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -196,7 +201,7 @@ def test_login_from_microsoft end def test_login_from_google - if @user = login_from(:google) + if (@user = login_from(:google)) redirect_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -204,7 +209,7 @@ def test_login_from_google end def test_login_from_liveid - if @user = login_from(:liveid) + if (@user = login_from(:liveid)) redirect_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -212,7 +217,7 @@ def test_login_from_liveid end def test_login_from_vk - if @user = login_from(:vk) + if (@user = login_from(:vk)) redirect_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -220,7 +225,7 @@ def test_login_from_vk end def test_login_from_jira - if @user = login_from(:jira) + if (@user = login_from(:jira)) redirect_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -228,7 +233,7 @@ def test_login_from_jira end def test_login_from_salesforce - if @user = login_from(:salesforce) + if (@user = login_from(:salesforce)) redirect_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -236,7 +241,7 @@ def test_login_from_salesforce end def test_login_from_slack - if @user = login_from(:slack) + if (@user = login_from(:slack)) redirect_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -244,7 +249,7 @@ def test_login_from_slack end def test_login_from_instagram - if @user = login_from(:instagram) + if (@user = login_from(:instagram)) redirect_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -252,7 +257,7 @@ def test_login_from_instagram end def test_return_to_with_external_twitter - if @user = login_from(:twitter) + if (@user = login_from(:twitter)) redirect_back_or_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -260,7 +265,7 @@ def test_return_to_with_external_twitter end def test_return_to_with_external_jira - if @user = login_from(:jira) + if (@user = login_from(:jira)) redirect_back_or_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -270,7 +275,7 @@ def test_return_to_with_external_jira alias test_return_to_with_external test_return_to_with_external_twitter def test_return_to_with_external_facebook - if @user = login_from(:facebook) + if (@user = login_from(:facebook)) redirect_back_or_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -278,7 +283,7 @@ def test_return_to_with_external_facebook end def test_return_to_with_external_github - if @user = login_from(:github) + if (@user = login_from(:github)) redirect_back_or_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -286,7 +291,7 @@ def test_return_to_with_external_github end def test_return_to_with_external_paypal - if @user = login_from(:paypal) + if (@user = login_from(:paypal)) redirect_back_or_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -294,7 +299,7 @@ def test_return_to_with_external_paypal end def test_return_to_with_external_wechat - if @user = login_from(:wechat) + if (@user = login_from(:wechat)) redirect_back_or_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -302,7 +307,7 @@ def test_return_to_with_external_wechat end def test_return_to_with_external_microsoft - if @user = login_from(:microsoft) + if (@user = login_from(:microsoft)) redirect_back_or_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -310,7 +315,7 @@ def test_return_to_with_external_microsoft end def test_return_to_with_external_google - if @user = login_from(:google) + if (@user = login_from(:google)) redirect_back_or_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -318,7 +323,7 @@ def test_return_to_with_external_google end def test_return_to_with_external_liveid - if @user = login_from(:liveid) + if (@user = login_from(:liveid)) redirect_back_or_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -326,7 +331,7 @@ def test_return_to_with_external_liveid end def test_return_to_with_external_vk - if @user = login_from(:vk) + if (@user = login_from(:vk)) redirect_back_or_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -334,7 +339,7 @@ def test_return_to_with_external_vk end def test_return_to_with_external_salesforce - if @user = login_from(:salesforce) + if (@user = login_from(:salesforce)) redirect_back_or_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -342,7 +347,7 @@ def test_return_to_with_external_salesforce end def test_return_to_with_external_slack - if @user = login_from(:slack) + if (@user = login_from(:slack)) redirect_back_or_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -350,7 +355,7 @@ def test_return_to_with_external_slack end def test_return_to_with_external_instagram - if @user = login_from(:instagram) + if (@user = login_from(:instagram)) redirect_back_or_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -360,7 +365,7 @@ def test_return_to_with_external_instagram def test_create_from_provider provider = params[:provider] login_from(provider) - if @user = create_from(provider) + if (@user = create_from(provider)) redirect_to 'bla', notice: 'Success!' else redirect_to 'blu', alert: 'Failed!' @@ -369,12 +374,13 @@ def test_create_from_provider def test_add_second_provider provider = params[:provider] - if logged_in? - if @user = add_provider_to_user(provider) - redirect_to 'bla', notice: 'Success!' - else - redirect_to 'blu', alert: 'Failed!' - end + + return unless logged_in? + + if (@user = add_provider_to_user(provider)) + redirect_to 'bla', notice: 'Success!' + else + redirect_to 'blu', alert: 'Failed!' end end diff --git a/spec/rails_app/app/mailers/sorcery_mailer.rb b/spec/rails_app/app/mailers/sorcery_mailer.rb index bf68d59e..88ca918f 100644 --- a/spec/rails_app/app/mailers/sorcery_mailer.rb +++ b/spec/rails_app/app/mailers/sorcery_mailer.rb @@ -28,7 +28,7 @@ def send_unlock_token_email(user) mail(to: user.email, subject: 'Your account has been locked due to many wrong logins') end - + def magic_login_email(user) @user = user @url = 'http://example.com/login' diff --git a/spec/rails_app/config/application.rb b/spec/rails_app/config/application.rb index 9d4f3dbd..0eeec0db 100644 --- a/spec/rails_app/config/application.rb +++ b/spec/rails_app/config/application.rb @@ -1,4 +1,4 @@ -require File.expand_path('../boot', __FILE__) +require File.expand_path('boot', __dir__) require 'action_controller/railtie' require 'action_mailer/railtie' @@ -6,16 +6,19 @@ Bundler.require :default, SORCERY_ORM +# rubocop:disable Lint/HandleExceptions begin require "#{SORCERY_ORM}/railtie" rescue LoadError + # TODO: Log this issue or change require scheme. end +# rubocop:enable Lint/HandleExceptions require 'sorcery' module AppRoot class Application < Rails::Application - config.autoload_paths.reject! { |p| p =~ /\/app\/(\w+)$/ && !%w(controllers helpers mailers views).include?(Regexp.last_match(1)) } + config.autoload_paths.reject! { |p| p =~ %r{/\/app\/(\w+)$/} && !%w[controllers helpers mailers views].include?(Regexp.last_match(1)) } config.autoload_paths += ["#{config.root}/app/#{SORCERY_ORM}"] # Settings in config/environments/* take precedence over those specified here. diff --git a/spec/rails_app/config/boot.rb b/spec/rails_app/config/boot.rb index eaa6ee87..e06e0ebe 100644 --- a/spec/rails_app/config/boot.rb +++ b/spec/rails_app/config/boot.rb @@ -1,4 +1,4 @@ # Set up gems listed in the Gemfile. -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../../Gemfile', __FILE__) +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../Gemfile', __dir__) require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) diff --git a/spec/rails_app/config/environment.rb b/spec/rails_app/config/environment.rb index 98a94c6d..37ea24a9 100644 --- a/spec/rails_app/config/environment.rb +++ b/spec/rails_app/config/environment.rb @@ -1,5 +1,5 @@ # Load the rails application -require File.expand_path('../application', __FILE__) +require File.expand_path('application', __dir__) # Initialize the rails application AppRoot::Application.initialize! diff --git a/spec/rails_app/db/migrate/activity_logging/20101224223624_add_activity_logging_to_users.rb b/spec/rails_app/db/migrate/activity_logging/20101224223624_add_activity_logging_to_users.rb index 52c0d9e9..ad96e090 100644 --- a/spec/rails_app/db/migrate/activity_logging/20101224223624_add_activity_logging_to_users.rb +++ b/spec/rails_app/db/migrate/activity_logging/20101224223624_add_activity_logging_to_users.rb @@ -5,11 +5,11 @@ def self.up add_column :users, :last_activity_at, :datetime, default: nil add_column :users, :last_login_from_ip_address, :string, default: nil - add_index :users, [:last_logout_at, :last_activity_at] + add_index :users, %i[last_logout_at last_activity_at] end def self.down - remove_index :users, [:last_logout_at, :last_activity_at] + remove_index :users, %i[last_logout_at last_activity_at] remove_column :users, :last_activity_at remove_column :users, :last_logout_at diff --git a/spec/rails_app/db/migrate/magic_login/20170924151831_add_magic_login_to_users.rb b/spec/rails_app/db/migrate/magic_login/20170924151831_add_magic_login_to_users.rb index 33196a6c..446d21fe 100644 --- a/spec/rails_app/db/migrate/magic_login/20170924151831_add_magic_login_to_users.rb +++ b/spec/rails_app/db/migrate/magic_login/20170924151831_add_magic_login_to_users.rb @@ -3,13 +3,13 @@ def self.up add_column :users, :magic_login_token, :string, default: nil add_column :users, :magic_login_token_expires_at, :datetime, default: nil add_column :users, :magic_login_email_sent_at, :datetime, default: nil - + add_index :users, :magic_login_token end - + def self.down remove_index :users, :magic_login_token - + remove_column :users, :magic_login_token remove_column :users, :magic_login_token_expires_at remove_column :users, :magic_login_email_sent_at diff --git a/spec/rails_app/db/schema.rb b/spec/rails_app/db/schema.rb index 7c319ca8..ebc42033 100644 --- a/spec/rails_app/db/schema.rb +++ b/spec/rails_app/db/schema.rb @@ -10,14 +10,12 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20101224223620) do - - create_table "users", :force => true do |t| - t.string "username" - t.string "email" - t.string "crypted_password" - t.datetime "created_at" - t.datetime "updated_at" +ActiveRecord::Schema.define(version: 20_101_224_223_620) do + create_table 'users', force: true do |t| + t.string 'username' + t.string 'email' + t.string 'crypted_password' + t.datetime 'created_at' + t.datetime 'updated_at' end - end diff --git a/spec/shared_examples/user_magic_login_shared_examples.rb b/spec/shared_examples/user_magic_login_shared_examples.rb index 758a2526..7a75c0fd 100644 --- a/spec/shared_examples/user_magic_login_shared_examples.rb +++ b/spec/shared_examples/user_magic_login_shared_examples.rb @@ -1,145 +1,145 @@ -shared_examples_for "magic_login_model" do - let(:user) {create_new_user} +shared_examples_for 'magic_login_model' do + let(:user) { create_new_user } before(:each) do User.sorcery_adapter.delete_all end - + context 'loaded plugin configuration' do - let(:config) {User.sorcery_config} - + let(:config) { User.sorcery_config } + before(:all) do sorcery_reload!([:magic_login]) end - + after(:each) do User.sorcery_config.reset! end - - describe "enables configuration options" do + + describe 'enables configuration options' do it do sorcery_model_property_set(:magic_login_token_attribute_name, :test_magic_login_token) expect(config.magic_login_token_attribute_name).to eq :test_magic_login_token end - + it do sorcery_model_property_set(:magic_login_token_expires_at_attribute_name, :test_magic_login_token_expires_at) expect(config.magic_login_token_expires_at_attribute_name).to eq :test_magic_login_token_expires_at end - + it do sorcery_model_property_set(:magic_login_email_sent_at_attribute_name, :test_magic_login_email_sent_at) expect(config.magic_login_email_sent_at_attribute_name).to eq :test_magic_login_email_sent_at end - + it do TestMailerClass = Class.new # need a mailer class to test sorcery_model_property_set(:magic_login_mailer_class, TestMailerClass) expect(config.magic_login_mailer_class).to eq TestMailerClass end - + it do sorcery_model_property_set(:magic_login_mailer_disabled, false) expect(config.magic_login_mailer_disabled).to eq false end - + it do sorcery_model_property_set(:magic_login_email_method_name, :test_magic_login_email) expect(config.magic_login_email_method_name).to eq :test_magic_login_email end - + it do - sorcery_model_property_set(:magic_login_expiration_period, 100000000) - expect(config.magic_login_expiration_period).to eq 100000000 + sorcery_model_property_set(:magic_login_expiration_period, 100_000_000) + expect(config.magic_login_expiration_period).to eq 100_000_000 end - + it do - sorcery_model_property_set(:magic_login_time_between_emails, 100000000) - expect(config.magic_login_time_between_emails).to eq 100000000 + sorcery_model_property_set(:magic_login_time_between_emails, 100_000_000) + expect(config.magic_login_time_between_emails).to eq 100_000_000 end end - describe "#generate_magic_login_token!" do - context "magic_login_token is nil" do + describe '#generate_magic_login_token!' do + context 'magic_login_token is nil' do it "magic_login_token_expires_at and magic_login_email_sent_at aren't nil " do user.generate_magic_login_token! expect(user.magic_login_token_expires_at).not_to be_nil expect(user.magic_login_email_sent_at).not_to be_nil end - - it "magic_login_token is different from the one before" do + + it 'magic_login_token is different from the one before' do token_before = user.magic_login_token user.generate_magic_login_token! expect(user.magic_login_token).not_to eq token_before end end - - context "magic_login_token is not nil" do - it "changes `user.magic_login_token`" do + + context 'magic_login_token is not nil' do + it 'changes `user.magic_login_token`' do token_before = user.magic_login_token user.generate_magic_login_token! expect(user.magic_login_token).not_to eq token_before end end end - - describe "#deliver_magic_login_instructions!" do - context "success" do + + describe '#deliver_magic_login_instructions!' do + context 'success' do before do - sorcery_model_property_set(:magic_login_time_between_emails, 30*60) + sorcery_model_property_set(:magic_login_time_between_emails, 30 * 60) sorcery_model_property_set(:magic_login_mailer_disabled, false) Timecop.travel(10.days.ago) do user.send(:"#{config.magic_login_email_sent_at_attribute_name}=", DateTime.now) end sorcery_model_property_set(:magic_login_mailer_class, ::SorceryMailer) end - + it do user.deliver_magic_login_instructions! expect(ActionMailer::Base.deliveries.size).to eq 1 end - + it do expect(user.deliver_magic_login_instructions!).to eq true end end - context "failure" do - context "magic_login_time_between_emails is nil" do - it "returns false" do + context 'failure' do + context 'magic_login_time_between_emails is nil' do + it 'returns false' do sorcery_model_property_set(:magic_login_time_between_emails, nil) expect(user.deliver_magic_login_instructions!).to eq false end end - - context "magic_login_email_sent_at is nil" do - it "returns false" do + + context 'magic_login_email_sent_at is nil' do + it 'returns false' do user.send(:"#{config.magic_login_email_sent_at_attribute_name}=", nil) expect(user.deliver_magic_login_instructions!).to eq false end end - - context "now is before magic_login_email_sent_at plus the interval" do - it "returns false" do + + context 'now is before magic_login_email_sent_at plus the interval' do + it 'returns false' do user.send(:"#{config.magic_login_email_sent_at_attribute_name}=", DateTime.now) - sorcery_model_property_set(:magic_login_time_between_emails, 30*60) + sorcery_model_property_set(:magic_login_time_between_emails, 30 * 60) expect(user.deliver_magic_login_instructions!).to eq false end end - - context "magic_login_mailer_disabled is true" do - it "returns false" do + + context 'magic_login_mailer_disabled is true' do + it 'returns false' do sorcery_model_property_set(:magic_login_mailer_disabled, true) expect(user.deliver_magic_login_instructions!).to eq false end end end end - - describe "#clear_magic_login_token!" do - it "makes magic_login_token_attribute_name and magic_login_token_expires_at_attribute_name nil" do - user.magic_login_token = "test_token" + + describe '#clear_magic_login_token!' do + it 'makes magic_login_token_attribute_name and magic_login_token_expires_at_attribute_name nil' do + user.magic_login_token = 'test_token' user.magic_login_token_expires_at = Time.now - + user.clear_magic_login_token! expect(user.magic_login_token).to eq nil diff --git a/spec/shared_examples/user_oauth_shared_examples.rb b/spec/shared_examples/user_oauth_shared_examples.rb index f06bbab8..3e6ec302 100644 --- a/spec/shared_examples/user_oauth_shared_examples.rb +++ b/spec/shared_examples/user_oauth_shared_examples.rb @@ -27,7 +27,7 @@ it "'load_from_provider' returns nil if user doesn't exist" do external_user - expect(User.load_from_provider(:twitter, 980342)).to be_nil + expect(User.load_from_provider(:twitter, 980_342)).to be_nil end end end diff --git a/spec/shared_examples/user_remember_me_shared_examples.rb b/spec/shared_examples/user_remember_me_shared_examples.rb index e7448d31..b31c4372 100644 --- a/spec/shared_examples/user_remember_me_shared_examples.rb +++ b/spec/shared_examples/user_remember_me_shared_examples.rb @@ -42,7 +42,7 @@ user.remember_me! end - expect(user.remember_me_token_expires_at.utc.to_s).to eq (ts + 2 * 60 * 60 * 24).utc.to_s + expect(user.remember_me_token_expires_at.utc.to_s).to eq((ts + 2 * 60 * 60 * 24).utc.to_s) end context 'when not persisting globally' do diff --git a/spec/shared_examples/user_reset_password_shared_examples.rb b/spec/shared_examples/user_reset_password_shared_examples.rb index c5a7af8f..be117034 100644 --- a/spec/shared_examples/user_reset_password_shared_examples.rb +++ b/spec/shared_examples/user_reset_password_shared_examples.rb @@ -245,7 +245,7 @@ end it 'does not send an email if time between emails has not passed since last email' do - sorcery_model_property_set(:reset_password_time_between_emails, 10000) + sorcery_model_property_set(:reset_password_time_between_emails, 10_000) old_size = ActionMailer::Base.deliveries.size user.deliver_reset_password_instructions! @@ -289,7 +289,7 @@ end it 'does not send an email if time between emails has not passed since last email' do - sorcery_model_property_set(:reset_password_time_between_emails, 10000) + sorcery_model_property_set(:reset_password_time_between_emails, 10_000) old_size = ActionMailer::Base.deliveries.size user.deliver_reset_password_instructions! @@ -326,7 +326,7 @@ end it 'returns false if time between emails has not passed since last email' do - sorcery_model_property_set(:reset_password_time_between_emails, 10000) + sorcery_model_property_set(:reset_password_time_between_emails, 10_000) user.deliver_reset_password_instructions! expect(user.deliver_reset_password_instructions!).to be false diff --git a/spec/shared_examples/user_shared_examples.rb b/spec/shared_examples/user_shared_examples.rb index e5d8e735..89e69f6f 100644 --- a/spec/shared_examples/user_shared_examples.rb +++ b/spec/shared_examples/user_shared_examples.rb @@ -228,10 +228,13 @@ class Admin2 < User; end expect(user).to receive(:save) { raise RuntimeError } + # rubocop:disable Lint/HandleExceptions begin user.save - rescue + rescue RuntimeError + # Intentionally force exception during save end + # rubocop:enable Lint/HandleExceptions expect(user.password).not_to be_nil end @@ -323,9 +326,11 @@ class Admin2 < User; end it 'use deliver_later' do sorcery_reload!( - [ - :user_activation, :user_activation_mailer, - :activation_needed_email_method_name, :email_delivery_method + %i[ + user_activation + user_activation_mailer + activation_needed_email_method_name + email_delivery_method ], user_activation_mailer: SorceryMailer, activation_needed_email_method_name: nil, @@ -340,9 +345,10 @@ class Admin2 < User; end it 'use deliver_now if rails version 4.2+' do allow(Rails).to receive(:version).and_return('4.2.0') sorcery_reload!( - [ - :user_activation, :user_activation_mailer, - :activation_needed_email_method_name + %i[ + user_activation + user_activation_mailer + activation_needed_email_method_name ], user_activation_mailer: SorceryMailer, activation_needed_email_method_name: nil @@ -355,9 +361,10 @@ class Admin2 < User; end it 'use deliver if rails version < 4.2' do allow(Rails).to receive(:version).and_return('4.1.0') sorcery_reload!( - [ - :user_activation, :user_activation_mailer, - :activation_needed_email_method_name + %i[ + user_activation + user_activation_mailer + activation_needed_email_method_name ], user_activation_mailer: SorceryMailer, activation_needed_email_method_name: nil @@ -503,7 +510,7 @@ def self.matches?(crypted, *tokens) end it 'find_by_username works as expected with multiple username attributes' do - sorcery_model_property_set(:username_attribute_names, [:username, :email]) + sorcery_model_property_set(:username_attribute_names, %i[username email]) expect(User.sorcery_adapter.find_by_username('gizmo')).to eq user end @@ -573,21 +580,21 @@ def self.matches?(crypted, *tokens) it 'does not create user when block return false' do expect do User.create_from_provider('facebook', '123', username: 'Noam Ben Ari') { false } - end.not_to change { User.count } + end.not_to(change { User.count }) end end end describe 'activation' do before(:each) do - sorcery_reload!([:user_activation, :external], user_activation_mailer: ::SorceryMailer) + sorcery_reload!(%i[user_activation external], user_activation_mailer: ::SorceryMailer) end after(:each) do User.sorcery_adapter.delete_all end - [:facebook, :github, :google, :liveid, :slack].each do |provider| + %i[facebook github google liveid slack].each do |provider| it 'does not send activation email to external users' do old_size = ActionMailer::Base.deliveries.size create_new_external_user(provider) diff --git a/spec/sorcery_crypto_providers_spec.rb b/spec/sorcery_crypto_providers_spec.rb index 604018fb..44610515 100644 --- a/spec/sorcery_crypto_providers_spec.rb +++ b/spec/sorcery_crypto_providers_spec.rb @@ -53,7 +53,7 @@ it 'matches password encrypted using salt and join token from upstream' do Sorcery::CryptoProviders::SHA1.join_token = 'test' - expect(Sorcery::CryptoProviders::SHA1.encrypt(%w(password gq18WBnJYNh2arkC1kgH))).to eq '894b5bf1643b8d0e1b2eaddb22426be7036dab70' + expect(Sorcery::CryptoProviders::SHA1.encrypt(%w[password gq18WBnJYNh2arkC1kgH])).to eq '894b5bf1643b8d0e1b2eaddb22426be7036dab70' end end diff --git a/spec/spec.opts b/spec/spec.opts index b3eb8b49..16f9cdb0 100644 --- a/spec/spec.opts +++ b/spec/spec.opts @@ -1,2 +1,2 @@ --color ---format documentation \ No newline at end of file +--format documentation diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 6c033504..bc5c48aa 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -40,7 +40,7 @@ class TestMailer < ActionMailer::Base; end if begin Module.const_defined?('::Rails::Controller::Testing') - rescue + rescue StandardError false end config.include ::Rails::Controller::Testing::TestProcess, type: :controller From ae83ac3181aed9696bf78ceabcce96bf735fd7ea Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Tue, 20 Nov 2018 23:15:19 +0000 Subject: [PATCH 044/112] Fix Sorcery::Controller not being correctly included by ActionController::Base (#159) --- lib/sorcery/engine.rb | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/sorcery/engine.rb b/lib/sorcery/engine.rb index 24e1ecd1..fd3bff16 100644 --- a/lib/sorcery/engine.rb +++ b/lib/sorcery/engine.rb @@ -8,12 +8,15 @@ class Engine < Rails::Engine config.sorcery = ::Sorcery::Controller::Config initializer 'extend Controller with sorcery' do - ActiveSupport.on_load(:action_controller) do - send(:include, Sorcery::Controller) + # TODO: Should this include a modified version of the helper methods? + if defined?(ActionController::API) + ActionController::API.send(:include, Sorcery::Controller) end - ActiveSupport.on_load(:action_controller_base) do - helper_method :current_user - helper_method :logged_in? + + if defined?(ActionController::Base) + ActionController::Base.send(:include, Sorcery::Controller) + ActionController::Base.helper_method :current_user + ActionController::Base.helper_method :logged_in? end end end From 11ca7f0580e4b0ba66b4cb758c3eb83e927cdae7 Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Thu, 29 Nov 2018 19:29:06 +0000 Subject: [PATCH 045/112] Add Auth0 as external provider (#160) * Add Auth0 provider to external Used github/google providers as reference, as well as the Omniauth/Auth0 strategy. * Add Auth0 initializer template * Require new auth0 provider file * Add specs for Auth0 provider * Update Auth0 to use sub instead of user_id https://community.auth0.com/t/authentication-api-userinfo-missing-the-user-id/8747/3 * Add 'sub' to spec hash to fix Auth0 specs * Update changelog --- CHANGELOG.md | 1 + .../sorcery/templates/initializer.rb | 7 +++ lib/sorcery/controller/submodules/external.rb | 1 + lib/sorcery/providers/auth0.rb | 46 +++++++++++++++++++ spec/controllers/controller_oauth2_spec.rb | 18 ++++++-- .../app/controllers/sorcery_controller.rb | 20 ++++++++ spec/rails_app/config/routes.rb | 3 ++ 7 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 lib/sorcery/providers/auth0.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index ef446e4b..0fc00889 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ * Remove Gemnasium badge [#140](https://github.com/Sorcery/sorcery/pull/140) * Add Instragram provider [#51](https://github.com/Sorcery/sorcery/pull/51) * Remove `publish_actions` permission for facebook [#139](https://github.com/Sorcery/sorcery/pull/139) +* Add Auth0 provider [#160](https://github.com/Sorcery/sorcery/pull/160) ## 0.12.0 diff --git a/lib/generators/sorcery/templates/initializer.rb b/lib/generators/sorcery/templates/initializer.rb index e08c3bcc..2b4ec3c1 100644 --- a/lib/generators/sorcery/templates/initializer.rb +++ b/lib/generators/sorcery/templates/initializer.rb @@ -145,6 +145,13 @@ # config.wechat.secret = "" # config.wechat.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=wechat" # + # For Auth0, site is required and should match the domain provided by Auth0. + # + # config.auth0.key = "" + # config.auth0.secret = "" + # config.auth0.callback_url = "https://0.0.0.0:3000/oauth/callback?provider=auth0" + # config.auth0.site = "https://example.auth0.com" + # # config.google.key = "" # config.google.secret = "" # config.google.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=google" diff --git a/lib/sorcery/controller/submodules/external.rb b/lib/sorcery/controller/submodules/external.rb index bb0b08d8..e48ff4cb 100644 --- a/lib/sorcery/controller/submodules/external.rb +++ b/lib/sorcery/controller/submodules/external.rb @@ -24,6 +24,7 @@ def self.included(base) require 'sorcery/providers/wechat' require 'sorcery/providers/microsoft' require 'sorcery/providers/instagram' + require 'sorcery/providers/auth0' Config.module_eval do class << self diff --git a/lib/sorcery/providers/auth0.rb b/lib/sorcery/providers/auth0.rb new file mode 100644 index 00000000..0978c2fa --- /dev/null +++ b/lib/sorcery/providers/auth0.rb @@ -0,0 +1,46 @@ +module Sorcery + module Providers + # This class adds support for OAuth with Auth0.com + # + # config.auth0.key = + # config.auth0.secret = + # config.auth0.domain = + # ... + # + class Auth0 < Base + include Protocols::Oauth2 + + attr_accessor :auth_path, :token_path, :user_info_path, :scope + + def initialize + super + + @auth_path = '/authorize' + @token_path = '/oauth/token' + @user_info_path = '/userinfo' + @scope = 'openid profile email' + end + + def get_user_hash(access_token) + response = access_token.get(user_info_path) + + auth_hash(access_token).tap do |h| + h[:user_info] = JSON.parse(response.body) + h[:uid] = h[:user_info]['sub'] + end + end + + def login_url(_params, _session) + authorize_url(authorize_url: auth_path) + end + + def process_callback(params, _session) + args = {}.tap do |a| + a[:code] = params[:code] if params[:code] + end + + get_access_token(args, token_url: token_path, token_method: :post) + end + end + end +end diff --git a/spec/controllers/controller_oauth2_spec.rb b/spec/controllers/controller_oauth2_spec.rb index b4b3d792..eb159009 100644 --- a/spec/controllers/controller_oauth2_spec.rb +++ b/spec/controllers/controller_oauth2_spec.rb @@ -155,7 +155,7 @@ expect(flash[:notice]).to eq 'Success!' end - %i[github google liveid vk salesforce paypal slack wechat microsoft instagram].each do |provider| + %i[github google liveid vk salesforce paypal slack wechat microsoft instagram auth0].each do |provider| describe "with #{provider}" do it 'login_at redirects correctly' do get :"login_at_test_#{provider}" @@ -215,6 +215,7 @@ wechat microsoft instagram + auth0 ] ) @@ -252,6 +253,10 @@ sorcery_controller_external_property_set(:instagram, :key, 'eYVNBjBDi33aa9GkA3w') sorcery_controller_external_property_set(:instagram, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') sorcery_controller_external_property_set(:instagram, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:auth0, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:auth0, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:auth0, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:auth0, :site, 'https://sorcery-test.auth0.com') end after(:each) do @@ -274,7 +279,7 @@ expect(ActionMailer::Base.deliveries.size).to eq old_size end - %i[github google liveid vk salesforce paypal wechat microsoft instagram].each do |provider| + %i[github google liveid vk salesforce paypal wechat microsoft instagram auth0].each do |provider| it "does not send activation email to external users (#{provider})" do old_size = ActionMailer::Base.deliveries.size create_new_external_user provider @@ -381,6 +386,7 @@ def stub_all_oauth2_requests! { 'id' => '123', 'user_id' => '123', # Needed for Salesforce + 'sub' => '123', # Needed for Auth0 'name' => 'Noam Ben Ari', 'first_name' => 'Noam', 'last_name' => 'Ben Ari', @@ -467,6 +473,7 @@ def set_external_property wechat microsoft instagram + auth0 ] ) sorcery_controller_external_property_set(:facebook, :key, 'eYVNBjBDi33aa9GkA3w') @@ -502,6 +509,10 @@ def set_external_property sorcery_controller_external_property_set(:instagram, :key, 'eYVNBjBDi33aa9GkA3w') sorcery_controller_external_property_set(:instagram, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') sorcery_controller_external_property_set(:instagram, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:auth0, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:auth0, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:auth0, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:auth0, :site, 'https://sorcery-test.auth0.com') end def provider_url(provider) @@ -515,7 +526,8 @@ def provider_url(provider) slack: "https://slack.com/oauth/authorize?client_id=#{::Sorcery::Controller::Config.slack.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=identity.basic%2C+identity.email&state", wechat: "https://open.weixin.qq.com/connect/qrconnect?appid=#{::Sorcery::Controller::Config.wechat.key}&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=snsapi_login&state=#wechat_redirect", microsoft: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=#{::Sorcery::Controller::Config.microsoft.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=openid+email+https%3A%2F%2Fgraph.microsoft.com%2FUser.Read&state", - instagram: "https://api.instagram.com/oauth/authorize?client_id=#{::Sorcery::Controller::Config.instagram.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=#{::Sorcery::Controller::Config.instagram.scope}&state" + instagram: "https://api.instagram.com/oauth/authorize?client_id=#{::Sorcery::Controller::Config.instagram.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=#{::Sorcery::Controller::Config.instagram.scope}&state", + auth0: "https://sorcery-test.auth0.com/authorize?client_id=#{::Sorcery::Controller::Config.auth0.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=openid+profile+email&state" }[provider] end end diff --git a/spec/rails_app/app/controllers/sorcery_controller.rb b/spec/rails_app/app/controllers/sorcery_controller.rb index 1fdf93a6..21b15f69 100644 --- a/spec/rails_app/app/controllers/sorcery_controller.rb +++ b/spec/rails_app/app/controllers/sorcery_controller.rb @@ -150,6 +150,10 @@ def login_at_test_instagram login_at(:instagram) end + def login_at_test_auth0 + login_at(:auth0) + end + def test_login_from_twitter if (@user = login_from(:twitter)) redirect_to 'bla', notice: 'Success!' @@ -256,6 +260,14 @@ def test_login_from_instagram end end + def test_login_from_auth0 + if (@user = login_from(:auth0)) + redirect_to 'bla', notice: 'Success!' + else + redirect_to 'blu', alert: 'Failed!' + end + end + def test_return_to_with_external_twitter if (@user = login_from(:twitter)) redirect_back_or_to 'bla', notice: 'Success!' @@ -362,6 +374,14 @@ def test_return_to_with_external_instagram end end + def test_return_to_with_external_auth0 + if (@user = login_from(:auth0)) + redirect_back_or_to 'bla', notice: 'Success!' + else + redirect_to 'blu', alert: 'Failed!' + end + end + def test_create_from_provider provider = params[:provider] login_from(provider) diff --git a/spec/rails_app/config/routes.rb b/spec/rails_app/config/routes.rb index de1b80d8..03d4eb2a 100644 --- a/spec/rails_app/config/routes.rb +++ b/spec/rails_app/config/routes.rb @@ -31,6 +31,7 @@ get :test_login_from_salesforce get :test_login_from_slack get :test_login_from_instagram + get :test_login_from_auth0 get :login_at_test get :login_at_test_twitter get :login_at_test_facebook @@ -45,6 +46,7 @@ get :login_at_test_salesforce get :login_at_test_slack get :login_at_test_instagram + get :login_at_test_auth0 get :test_return_to_with_external get :test_return_to_with_external_twitter get :test_return_to_with_external_facebook @@ -59,6 +61,7 @@ get :test_return_to_with_external_salesforce get :test_return_to_with_external_slack get :test_return_to_with_external_instagram + get :test_return_to_with_external_auth0 get :test_http_basic_auth get :some_action_making_a_non_persisted_change_to_the_user post :test_login_with_remember From 3a7e3519af16999c25e326145b4a821ba374ec41 Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Thu, 29 Nov 2018 19:50:36 +0000 Subject: [PATCH 046/112] Update changelog (#161) --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fc00889..600de55d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ * Add support for Rails 5.2 / Ruby 2.5 [#129](https://github.com/Sorcery/sorcery/pull/129) * Fix migration files not being generated [#128](https://github.com/Sorcery/sorcery/pull/128) -* Add support for ActionController::API [#133](https://github.com/Sorcery/sorcery/pull/133) +* Add support for ActionController::API [#133](https://github.com/Sorcery/sorcery/pull/133), [#150](https://github.com/Sorcery/sorcery/pull/150), [#159](https://github.com/Sorcery/sorcery/pull/159) * Update activation email to use after_commit callback [#130](https://github.com/Sorcery/sorcery/pull/130) * Add opt-in `invalidate_active_sessions!` method [#110](https://github.com/Sorcery/sorcery/pull/110) * Pass along `remember_me` to `#auto_login` [#136](https://github.com/Sorcery/sorcery/pull/136) @@ -12,6 +12,7 @@ * Remove Gemnasium badge [#140](https://github.com/Sorcery/sorcery/pull/140) * Add Instragram provider [#51](https://github.com/Sorcery/sorcery/pull/51) * Remove `publish_actions` permission for facebook [#139](https://github.com/Sorcery/sorcery/pull/139) +* Prepare for 1.0.0 [#157](https://github.com/Sorcery/sorcery/pull/157) * Add Auth0 provider [#160](https://github.com/Sorcery/sorcery/pull/160) ## 0.12.0 From 88181d320388b7708dfdcc38efc265acd909f1aa Mon Sep 17 00:00:00 2001 From: Joshua Buker Date: Thu, 29 Nov 2018 12:07:58 -0800 Subject: [PATCH 047/112] Release 0.13.0 --- CHANGELOG.md | 2 ++ lib/sorcery/version.rb | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 600de55d..f89dc22d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## HEAD +## 0.13.0 + * Add support for Rails 5.2 / Ruby 2.5 [#129](https://github.com/Sorcery/sorcery/pull/129) * Fix migration files not being generated [#128](https://github.com/Sorcery/sorcery/pull/128) * Add support for ActionController::API [#133](https://github.com/Sorcery/sorcery/pull/133), [#150](https://github.com/Sorcery/sorcery/pull/150), [#159](https://github.com/Sorcery/sorcery/pull/159) diff --git a/lib/sorcery/version.rb b/lib/sorcery/version.rb index cf14d722..74a652ad 100644 --- a/lib/sorcery/version.rb +++ b/lib/sorcery/version.rb @@ -1,3 +1,3 @@ module Sorcery - VERSION = '0.12.0'.freeze + VERSION = '0.13.0'.freeze end From 250fd60b4652bd6fbd45ac1b0e189926d2a900aa Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Fri, 25 Jan 2019 16:40:51 +0000 Subject: [PATCH 048/112] Remove before_install entirely from travis.yml per @BanzaiMan feedback (#171) * Remove before_install entirely from travis.yml per @BanzaiMan feedback * Fix version conflict with Rails 4 gemfiles Rails 4 requires bundler <2.0, but Rails 4 is on the way out, and Rails 6 is on the horizon. I think it makes sense to drop support rather than fight the TravisCI configuration between versions. * Update CHANGELOG.md --- .travis.yml | 26 ------------------------- CHANGELOG.md | 2 ++ gemfiles/active_record_rails_40.gemfile | 6 ------ gemfiles/active_record_rails_41.gemfile | 6 ------ gemfiles/active_record_rails_42.gemfile | 6 ------ 5 files changed, 2 insertions(+), 44 deletions(-) delete mode 100644 gemfiles/active_record_rails_40.gemfile delete mode 100644 gemfiles/active_record_rails_41.gemfile delete mode 100644 gemfiles/active_record_rails_42.gemfile diff --git a/.travis.yml b/.travis.yml index cd3c6641..a6facab1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,32 +7,6 @@ rvm: gemfile: - Gemfile - - gemfiles/active_record_rails_40.gemfile - - gemfiles/active_record_rails_41.gemfile - - gemfiles/active_record_rails_42.gemfile before_script: - mysql -e 'create database sorcery_test;' - -before_install: - - rvm get stable --auto-dotfiles - - gem update bundler - -matrix: - exclude: - - rvm: 2.2.9 - gemfile: gemfiles/active_record_rails_40.gemfile - - rvm: 2.3.6 - gemfile: gemfiles/active_record_rails_40.gemfile - - rvm: 2.4.3 - gemfile: gemfiles/active_record_rails_40.gemfile - - rvm: 2.4.3 - gemfile: gemfiles/active_record_rails_41.gemfile - - rvm: 2.4.3 - gemfile: gemfiles/active_record_rails_42.gemfile - - rvm: 2.5.0 - gemfile: gemfiles/active_record_rails_40.gemfile - - rvm: 2.5.0 - gemfile: gemfiles/active_record_rails_41.gemfile - - rvm: 2.5.0 - gemfile: gemfiles/active_record_rails_42.gemfile diff --git a/CHANGELOG.md b/CHANGELOG.md index f89dc22d..b259df49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## HEAD +* Support for Rails 4.2 and lower soft-dropped [#171](https://github.com/Sorcery/sorcery/pull/171) + ## 0.13.0 * Add support for Rails 5.2 / Ruby 2.5 [#129](https://github.com/Sorcery/sorcery/pull/129) diff --git a/gemfiles/active_record_rails_40.gemfile b/gemfiles/active_record_rails_40.gemfile deleted file mode 100644 index ffaf538a..00000000 --- a/gemfiles/active_record_rails_40.gemfile +++ /dev/null @@ -1,6 +0,0 @@ -source 'https://rubygems.org' - -gem 'rails', '~> 4.0.1' -gem 'sqlite3', platform: :mri - -gemspec path: '..' diff --git a/gemfiles/active_record_rails_41.gemfile b/gemfiles/active_record_rails_41.gemfile deleted file mode 100644 index 67180498..00000000 --- a/gemfiles/active_record_rails_41.gemfile +++ /dev/null @@ -1,6 +0,0 @@ -source 'https://rubygems.org' - -gem 'rails', '~> 4.1.0' -gem 'sqlite3', platform: :mri - -gemspec path: '..' diff --git a/gemfiles/active_record_rails_42.gemfile b/gemfiles/active_record_rails_42.gemfile deleted file mode 100644 index 0fec5757..00000000 --- a/gemfiles/active_record_rails_42.gemfile +++ /dev/null @@ -1,6 +0,0 @@ -source 'https://rubygems.org' - -gem 'rails', '~> 4.2.0' -gem 'sqlite3', platform: :mri - -gemspec path: '..' From ebb483a31ca1d01d0ef61561617c9d5d625d76a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mladen=20Ili=C4=87?= Date: Fri, 25 Jan 2019 17:59:01 +0100 Subject: [PATCH 049/112] Update migration templates to use new hash syntax (#170) --- .../sorcery/templates/migration/activity_logging.rb | 8 ++++---- .../sorcery/templates/migration/brute_force_protection.rb | 6 +++--- lib/generators/sorcery/templates/migration/core.rb | 4 ++-- lib/generators/sorcery/templates/migration/external.rb | 6 +++--- lib/generators/sorcery/templates/migration/magic_login.rb | 6 +++--- lib/generators/sorcery/templates/migration/remember_me.rb | 4 ++-- .../sorcery/templates/migration/reset_password.rb | 8 ++++---- .../sorcery/templates/migration/user_activation.rb | 6 +++--- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/generators/sorcery/templates/migration/activity_logging.rb b/lib/generators/sorcery/templates/migration/activity_logging.rb index e308ed4d..c1b3edd6 100644 --- a/lib/generators/sorcery/templates/migration/activity_logging.rb +++ b/lib/generators/sorcery/templates/migration/activity_logging.rb @@ -1,9 +1,9 @@ class SorceryActivityLogging < <%= migration_class_name %> def change - add_column :<%= model_class_name.tableize %>, :last_login_at, :datetime, :default => nil - add_column :<%= model_class_name.tableize %>, :last_logout_at, :datetime, :default => nil - add_column :<%= model_class_name.tableize %>, :last_activity_at, :datetime, :default => nil - add_column :<%= model_class_name.tableize %>, :last_login_from_ip_address, :string, :default => nil + add_column :<%= model_class_name.tableize %>, :last_login_at, :datetime, default: nil + add_column :<%= model_class_name.tableize %>, :last_logout_at, :datetime, default: nil + add_column :<%= model_class_name.tableize %>, :last_activity_at, :datetime, default: nil + add_column :<%= model_class_name.tableize %>, :last_login_from_ip_address, :string, default: nil add_index :<%= model_class_name.tableize %>, [:last_logout_at, :last_activity_at] end diff --git a/lib/generators/sorcery/templates/migration/brute_force_protection.rb b/lib/generators/sorcery/templates/migration/brute_force_protection.rb index 33cf6390..64ea19a8 100644 --- a/lib/generators/sorcery/templates/migration/brute_force_protection.rb +++ b/lib/generators/sorcery/templates/migration/brute_force_protection.rb @@ -1,8 +1,8 @@ class SorceryBruteForceProtection < <%= migration_class_name %> def change - add_column :<%= model_class_name.tableize %>, :failed_logins_count, :integer, :default => 0 - add_column :<%= model_class_name.tableize %>, :lock_expires_at, :datetime, :default => nil - add_column :<%= model_class_name.tableize %>, :unlock_token, :string, :default => nil + add_column :<%= model_class_name.tableize %>, :failed_logins_count, :integer, default: 0 + add_column :<%= model_class_name.tableize %>, :lock_expires_at, :datetime, default: nil + add_column :<%= model_class_name.tableize %>, :unlock_token, :string, default: nil add_index :<%= model_class_name.tableize %>, :unlock_token end diff --git a/lib/generators/sorcery/templates/migration/core.rb b/lib/generators/sorcery/templates/migration/core.rb index af5f721c..1ca5287b 100644 --- a/lib/generators/sorcery/templates/migration/core.rb +++ b/lib/generators/sorcery/templates/migration/core.rb @@ -1,11 +1,11 @@ class SorceryCore < <%= migration_class_name %> def change create_table :<%= model_class_name.tableize %> do |t| - t.string :email, :null => false + t.string :email, null: false t.string :crypted_password t.string :salt - t.timestamps :null => false + t.timestamps null: false end add_index :<%= model_class_name.tableize %>, :email, unique: true diff --git a/lib/generators/sorcery/templates/migration/external.rb b/lib/generators/sorcery/templates/migration/external.rb index 8ac7510b..08541f3b 100644 --- a/lib/generators/sorcery/templates/migration/external.rb +++ b/lib/generators/sorcery/templates/migration/external.rb @@ -1,10 +1,10 @@ class SorceryExternal < <%= migration_class_name %> def change create_table :authentications do |t| - t.integer :<%= model_class_name.tableize.singularize %>_id, :null => false - t.string :provider, :uid, :null => false + t.integer :<%= model_class_name.tableize.singularize %>_id, null: false + t.string :provider, :uid, null: false - t.timestamps :null => false + t.timestamps null: false end add_index :authentications, [:provider, :uid] diff --git a/lib/generators/sorcery/templates/migration/magic_login.rb b/lib/generators/sorcery/templates/migration/magic_login.rb index 6a1c9512..b9a10a6c 100644 --- a/lib/generators/sorcery/templates/migration/magic_login.rb +++ b/lib/generators/sorcery/templates/migration/magic_login.rb @@ -1,8 +1,8 @@ class SorceryMagicLogin < <%= migration_class_name %> def change - add_column :<%= model_class_name.tableize %>, :magic_login_token, :string, :default => nil - add_column :<%= model_class_name.tableize %>, :magic_login_token_expires_at, :datetime, :default => nil - add_column :<%= model_class_name.tableize %>, :magic_login_email_sent_at, :datetime, :default => nil + add_column :<%= model_class_name.tableize %>, :magic_login_token, :string, default: nil + add_column :<%= model_class_name.tableize %>, :magic_login_token_expires_at, :datetime, default: nil + add_column :<%= model_class_name.tableize %>, :magic_login_email_sent_at, :datetime, default: nil add_index :<%= model_class_name.tableize %>, :magic_login_token end diff --git a/lib/generators/sorcery/templates/migration/remember_me.rb b/lib/generators/sorcery/templates/migration/remember_me.rb index 605660a3..a27f7b69 100644 --- a/lib/generators/sorcery/templates/migration/remember_me.rb +++ b/lib/generators/sorcery/templates/migration/remember_me.rb @@ -1,7 +1,7 @@ class SorceryRememberMe < <%= migration_class_name %> def change - add_column :<%= model_class_name.tableize %>, :remember_me_token, :string, :default => nil - add_column :<%= model_class_name.tableize %>, :remember_me_token_expires_at, :datetime, :default => nil + add_column :<%= model_class_name.tableize %>, :remember_me_token, :string, default: nil + add_column :<%= model_class_name.tableize %>, :remember_me_token_expires_at, :datetime, default: nil add_index :<%= model_class_name.tableize %>, :remember_me_token end diff --git a/lib/generators/sorcery/templates/migration/reset_password.rb b/lib/generators/sorcery/templates/migration/reset_password.rb index 21d32678..9cf66c8c 100644 --- a/lib/generators/sorcery/templates/migration/reset_password.rb +++ b/lib/generators/sorcery/templates/migration/reset_password.rb @@ -1,9 +1,9 @@ class SorceryResetPassword < <%= migration_class_name %> def change - add_column :<%= model_class_name.tableize %>, :reset_password_token, :string, :default => nil - add_column :<%= model_class_name.tableize %>, :reset_password_token_expires_at, :datetime, :default => nil - add_column :<%= model_class_name.tableize %>, :reset_password_email_sent_at, :datetime, :default => nil - add_column :<%= model_class_name.tableize %>, :access_count_to_reset_password_page, :integer, :default => 0 + add_column :<%= model_class_name.tableize %>, :reset_password_token, :string, default: nil + add_column :<%= model_class_name.tableize %>, :reset_password_token_expires_at, :datetime, default: nil + add_column :<%= model_class_name.tableize %>, :reset_password_email_sent_at, :datetime, default: nil + add_column :<%= model_class_name.tableize %>, :access_count_to_reset_password_page, :integer, default: 0 add_index :<%= model_class_name.tableize %>, :reset_password_token end diff --git a/lib/generators/sorcery/templates/migration/user_activation.rb b/lib/generators/sorcery/templates/migration/user_activation.rb index c3a56547..cc5dc19d 100644 --- a/lib/generators/sorcery/templates/migration/user_activation.rb +++ b/lib/generators/sorcery/templates/migration/user_activation.rb @@ -1,8 +1,8 @@ class SorceryUserActivation < <%= migration_class_name %> def change - add_column :<%= model_class_name.tableize %>, :activation_state, :string, :default => nil - add_column :<%= model_class_name.tableize %>, :activation_token, :string, :default => nil - add_column :<%= model_class_name.tableize %>, :activation_token_expires_at, :datetime, :default => nil + add_column :<%= model_class_name.tableize %>, :activation_state, :string, default: nil + add_column :<%= model_class_name.tableize %>, :activation_token, :string, default: nil + add_column :<%= model_class_name.tableize %>, :activation_token_expires_at, :datetime, default: nil add_index :<%= model_class_name.tableize %>, :activation_token end From 012172fc89fded5f3faa7f70c7e391c898012238 Mon Sep 17 00:00:00 2001 From: kissrobber Date: Sat, 16 Mar 2019 17:29:10 +0900 Subject: [PATCH 050/112] update_failed_logins_count is called twice when login failed (#163) * Fix engine initializer * Update controller callbacks to safeguard against double inclusion * Update sqlite3 dependency per TravisCI error --- Gemfile | 2 +- .../controller/submodules/activity_logging.rb | 13 ++++++++++--- .../controller/submodules/brute_force_protection.rb | 10 +++++++--- .../controller/submodules/http_basic_auth.rb | 5 ++++- lib/sorcery/controller/submodules/remember_me.rb | 9 +++++++-- .../controller/submodules/session_timeout.rb | 9 +++++++-- 6 files changed, 36 insertions(+), 12 deletions(-) diff --git a/Gemfile b/Gemfile index 6afda476..9ee5d84f 100644 --- a/Gemfile +++ b/Gemfile @@ -3,6 +3,6 @@ source 'https://rubygems.org' gem 'pry' gem 'rails', '~> 5.2.0' gem 'rails-controller-testing' -gem 'sqlite3' +gem 'sqlite3', '~> 1.3.6' gemspec diff --git a/lib/sorcery/controller/submodules/activity_logging.rb b/lib/sorcery/controller/submodules/activity_logging.rb index eb04cf0b..58782af6 100644 --- a/lib/sorcery/controller/submodules/activity_logging.rb +++ b/lib/sorcery/controller/submodules/activity_logging.rb @@ -30,9 +30,16 @@ def merge_activity_logging_defaults! end merge_activity_logging_defaults! end - Config.after_login << :register_login_time_to_db - Config.after_login << :register_last_ip_address - Config.before_logout << :register_logout_time_to_db + # FIXME: There is likely a more elegant way to safeguard these callbacks. + unless Config.after_login.include?(:register_login_time_to_db) + Config.after_login << :register_login_time_to_db + end + unless Config.after_login.include?(:register_last_ip_address) + Config.after_login << :register_last_ip_address + end + unless Config.before_logout.include?(:register_logout_time_to_db) + Config.before_logout << :register_logout_time_to_db + end base.after_action :register_last_activity_time_to_db end diff --git a/lib/sorcery/controller/submodules/brute_force_protection.rb b/lib/sorcery/controller/submodules/brute_force_protection.rb index e4756b7b..67b12f87 100644 --- a/lib/sorcery/controller/submodules/brute_force_protection.rb +++ b/lib/sorcery/controller/submodules/brute_force_protection.rb @@ -10,9 +10,13 @@ module Submodules module BruteForceProtection def self.included(base) base.send(:include, InstanceMethods) - - Config.after_login << :reset_failed_logins_count! - Config.after_failed_login << :update_failed_logins_count! + # FIXME: There is likely a more elegant way to safeguard these callbacks. + unless Config.after_login.include?(:reset_failed_logins_count!) + Config.after_login << :reset_failed_logins_count! + end + unless Config.after_failed_login.include?(:update_failed_logins_count!) + Config.after_failed_login << :update_failed_logins_count! + end end module InstanceMethods diff --git a/lib/sorcery/controller/submodules/http_basic_auth.rb b/lib/sorcery/controller/submodules/http_basic_auth.rb index 56bf5346..e4cb265d 100644 --- a/lib/sorcery/controller/submodules/http_basic_auth.rb +++ b/lib/sorcery/controller/submodules/http_basic_auth.rb @@ -19,7 +19,10 @@ def merge_http_basic_auth_defaults! end merge_http_basic_auth_defaults! end - Config.login_sources << :login_from_basic_auth + # FIXME: There is likely a more elegant way to safeguard these callbacks. + unless Config.login_sources.include?(:login_from_basic_auth) + Config.login_sources << :login_from_basic_auth + end end module InstanceMethods diff --git a/lib/sorcery/controller/submodules/remember_me.rb b/lib/sorcery/controller/submodules/remember_me.rb index 1bcad7e5..fa93f4e9 100644 --- a/lib/sorcery/controller/submodules/remember_me.rb +++ b/lib/sorcery/controller/submodules/remember_me.rb @@ -17,8 +17,13 @@ def merge_remember_me_defaults! end merge_remember_me_defaults! end - Config.login_sources << :login_from_cookie - Config.before_logout << :forget_me! + # FIXME: There is likely a more elegant way to safeguard these callbacks. + unless Config.login_sources.include?(:login_from_cookie) + Config.login_sources << :login_from_cookie + end + unless Config.before_logout.include?(:forget_me!) + Config.before_logout << :forget_me! + end end module InstanceMethods diff --git a/lib/sorcery/controller/submodules/session_timeout.rb b/lib/sorcery/controller/submodules/session_timeout.rb index b0cc65b9..0b0a02c2 100644 --- a/lib/sorcery/controller/submodules/session_timeout.rb +++ b/lib/sorcery/controller/submodules/session_timeout.rb @@ -23,8 +23,13 @@ def merge_session_timeout_defaults! end merge_session_timeout_defaults! end - Config.after_login << :register_login_time - Config.after_remember_me << :register_login_time + # FIXME: There is likely a more elegant way to safeguard these callbacks. + unless Config.after_login.include?(:register_login_time) + Config.after_login << :register_login_time + end + unless Config.after_remember_me.include?(:register_login_time) + Config.after_remember_me << :register_login_time + end base.prepend_before_action :validate_session end From a87c0693c941fedfd3c5896179aea8cbc4dedd38 Mon Sep 17 00:00:00 2001 From: Ryo Okamoto Date: Sat, 16 Mar 2019 17:31:26 +0900 Subject: [PATCH 051/112] fix CipherError class name (#142) --- lib/sorcery/crypto_providers/aes256.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sorcery/crypto_providers/aes256.rb b/lib/sorcery/crypto_providers/aes256.rb index 81dd2b68..4fc542da 100644 --- a/lib/sorcery/crypto_providers/aes256.rb +++ b/lib/sorcery/crypto_providers/aes256.rb @@ -29,7 +29,7 @@ def encrypt(*tokens) def matches?(crypted, *tokens) decrypt(crypted) == tokens.join - rescue OpenSSL::CipherError + rescue OpenSSL::Cipher::CipherError false end From c6680472c8858a9e53cd6f660f4cf8d3a70f4acb Mon Sep 17 00:00:00 2001 From: Steven Hoffman Date: Sat, 16 Mar 2019 01:34:37 -0700 Subject: [PATCH 052/112] Allow load_from_magic_login_token to accept a block (#152) --- lib/sorcery/model/submodules/magic_login.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/sorcery/model/submodules/magic_login.rb b/lib/sorcery/model/submodules/magic_login.rb index 409cbdce..629d5d68 100644 --- a/lib/sorcery/model/submodules/magic_login.rb +++ b/lib/sorcery/model/submodules/magic_login.rb @@ -52,10 +52,13 @@ def self.included(base) module ClassMethods # Find user by token, also checks for expiration. # Returns the user if token found and is valid. - def load_from_magic_login_token(token) - token_attr_name = @sorcery_config.magic_login_token_attribute_name - token_expiration_date_attr = @sorcery_config.magic_login_token_expires_at_attribute_name - load_from_token(token, token_attr_name, token_expiration_date_attr) + def load_from_magic_login_token(token, &block) + load_from_token( + token, + @sorcery_config.magic_login_token_attribute_name, + @sorcery_config.magic_login_token_expires_at_attribute_name, + &block + ) end protected From 283f0236a68c1a703fbb48d58e5bd3736989698b Mon Sep 17 00:00:00 2001 From: Valentine C Date: Sat, 16 Mar 2019 16:37:19 +0800 Subject: [PATCH 053/112] Clean up initializer comments (#153) * Add magic_login to initializer comments * Clean up initializer comments --- .../sorcery/templates/initializer.rb | 159 +++++++++--------- 1 file changed, 80 insertions(+), 79 deletions(-) diff --git a/lib/generators/sorcery/templates/initializer.rb b/lib/generators/sorcery/templates/initializer.rb index 2b4ec3c1..b5be6ee2 100644 --- a/lib/generators/sorcery/templates/initializer.rb +++ b/lib/generators/sorcery/templates/initializer.rb @@ -1,7 +1,9 @@ # The first thing you need to configure is which modules you need in your app. # The default is nothing which will include only core features (password encryption, login/logout). +# # Available submodules are: :user_activation, :http_basic_auth, :remember_me, -# :reset_password, :session_timeout, :brute_force_protection, :activity_logging, :external +# :reset_password, :session_timeout, :brute_force_protection, :activity_logging, +# :magic_login, :external Rails.application.config.sorcery.submodules = [] # Here you can configure each submodule's features. @@ -13,8 +15,8 @@ # # config.not_authenticated_action = - # When a non logged in user tries to enter a page that requires login, save - # the URL he wanted to reach, and send him there after login, using 'redirect_back_or_to'. + # When a non logged-in user tries to enter a page that requires login, save + # the URL he wants to reach, and send him there after login, using 'redirect_back_or_to'. # Default: `true` # # config.save_return_to_url = @@ -46,7 +48,7 @@ # # config.session_timeout_from_last_action = - # Invalidate active sessions Requires an `invalidate_sessions_before` timestamp column + # Invalidate active sessions. Requires an `invalidate_sessions_before` timestamp column # Default: `false` # # config.session_timeout_invalidate_active_sessions_enabled = @@ -58,23 +60,24 @@ # config.controller_to_realm_map = # -- activity logging -- - # will register the time of last user login, every login. + # Will register the time of last user login, every login. # Default: `true` # # config.register_login_time = - # will register the time of last user logout, every logout. + # Will register the time of last user logout, every logout. # Default: `true` # # config.register_logout_time = - # will register the time of last user action, every action. + # Will register the time of last user action, every action. # Default: `true` # # config.register_last_activity_time = # -- external -- - # What providers are supported by this app, i.e. [:twitter, :facebook, :github, :linkedin, :xing, :google, :liveid, :salesforce, :slack] . + # What providers are supported by this app + # i.e. [:twitter, :facebook, :github, :linkedin, :xing, :google, :liveid, :salesforce, :slack]. # Default: `[]` # # config.external_providers = @@ -107,7 +110,7 @@ # # # Twitter will not accept any requests nor redirect uri containing localhost, - # make sure you use 0.0.0.0:3000 to access your app in development + # Make sure you use 0.0.0.0:3000 to access your app in development # # config.twitter.key = "" # config.twitter.secret = "" @@ -159,7 +162,8 @@ # config.google.scope = "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile" # # For Microsoft Graph, the key will be your App ID, and the secret will be your app password/public key. - # The callback URL "can't contain a query string or invalid special characters", see: https://docs.microsoft.com/en-us/azure/active-directory/active-directory-v2-limitations#restrictions-on-redirect-uris + # The callback URL "can't contain a query string or invalid special characters" + # See: https://docs.microsoft.com/en-us/azure/active-directory/active-directory-v2-limitations#restrictions-on-redirect-uris # More information at https://graph.microsoft.io/en-us/docs # # config.microsoft.key = "" @@ -190,7 +194,7 @@ # For information about JIRA API: # https://developer.atlassian.com/display/JIRADEV/JIRA+REST+API+Example+-+OAuth+authentication - # to obtain the consumer key and the public key you can use the jira-ruby gem https://github.com/sumoheavy/jira-ruby + # To obtain the consumer key and the public key you can use the jira-ruby gem https://github.com/sumoheavy/jira-ruby # or run openssl req -x509 -nodes -newkey rsa:1024 -sha1 -keyout rsakey.pem -out rsacert.pem to obtain the public key # Make sure you have configured the application link properly @@ -203,7 +207,7 @@ # For information about Salesforce API: # https://developer.salesforce.com/signup & # https://www.salesforce.com/us/developer/docs/api_rest/ - # Salesforce callback_url must be https. You can run the following to generate self-signed ssl cert + # Salesforce callback_url must be https. You can run the following to generate self-signed ssl cert: # openssl req -new -newkey rsa:2048 -sha1 -days 365 -nodes -x509 -keyout server.key -out server.crt # Make sure you have configured the application link properly # config.salesforce.key = '123123' @@ -215,227 +219,223 @@ # --- user config --- config.user_config do |user| # -- core -- - # specify username attributes, for example: [:username, :email]. + # Specify username attributes, for example: [:username, :email]. # Default: `[:email]` # # user.username_attribute_names = - # change *virtual* password attribute, the one which is used until an encrypted one is generated. + # Change *virtual* password attribute, the one which is used until an encrypted one is generated. # Default: `:password` # # user.password_attribute_name = - # downcase the username before trying to authenticate, default is false + # Downcase the username before trying to authenticate, default is false # Default: `false` # # user.downcase_username_before_authenticating = - # change default email attribute. + # Change default email attribute. # Default: `:email` # # user.email_attribute_name = - # change default crypted_password attribute. + # Change default crypted_password attribute. # Default: `:crypted_password` # # user.crypted_password_attribute_name = - # what pattern to use to join the password with the salt + # What pattern to use to join the password with the salt # Default: `""` # # user.salt_join_token = - # change default salt attribute. + # Change default salt attribute. # Default: `:salt` # # user.salt_attribute_name = - # how many times to apply encryption to the password. + # How many times to apply encryption to the password. # Default: 1 in test env, `nil` otherwise # user.stretches = 1 if Rails.env.test? - # encryption key used to encrypt reversible encryptions such as AES256. + # Encryption key used to encrypt reversible encryptions such as AES256. # WARNING: If used for users' passwords, changing this key will leave passwords undecryptable! # Default: `nil` # # user.encryption_key = - # use an external encryption class. + # Use an external encryption class. # Default: `nil` # # user.custom_encryption_provider = - # encryption algorithm name. See 'encryption_algorithm=' for available options. + # Encryption algorithm name. See 'encryption_algorithm=' for available options. # Default: `:bcrypt` # # user.encryption_algorithm = - # make this configuration inheritable for subclasses. Useful for ActiveRecord's STI. + # Make this configuration inheritable for subclasses. Useful for ActiveRecord's STI. # Default: `false` # # user.subclasses_inherit_config = # -- remember_me -- # How long in seconds the session length will be - # Default: `604800` + # Default: `60 * 60 * 24 * 7` # # user.remember_me_for = - # when true sorcery will persist a single remember me token for all - # logins/logouts (supporting remembering on multiple browsers simultaneously). + # When true, sorcery will persist a single remember me token for all + # logins/logouts (to support remembering on multiple browsers simultaneously). # Default: false # # user.remember_me_token_persist_globally = # -- user_activation -- - # the attribute name to hold activation state (active/pending). + # The attribute name to hold activation state (active/pending). # Default: `:activation_state` # # user.activation_state_attribute_name = - # the attribute name to hold activation code (sent by email). + # The attribute name to hold activation code (sent by email). # Default: `:activation_token` # # user.activation_token_attribute_name = - # the attribute name to hold activation code expiration date. + # The attribute name to hold activation code expiration date. # Default: `:activation_token_expires_at` # # user.activation_token_expires_at_attribute_name = - # how many seconds before the activation code expires. nil for never expires. + # How many seconds before the activation code expires. nil for never expires. # Default: `nil` # # user.activation_token_expiration_period = - # your mailer class. Required. + # REQUIRED: + # User activation mailer class. # Default: `nil` # # user.user_activation_mailer = - # when true sorcery will not automatically - # email activation details and allow you to - # manually handle how and when email is sent. + # When true, sorcery will not automatically + # send the activation details email, and allow you to + # manually handle how and when the email is sent. # Default: `false` # # user.activation_mailer_disabled = - # method to send email related + # Method to send email related # options: `:deliver_later`, `:deliver_now`, `:deliver` # Default: :deliver (Rails version < 4.2) or :deliver_now (Rails version 4.2+) # # user.email_delivery_method = - # activation needed email method on your mailer class. + # Activation needed email method on your mailer class. # Default: `:activation_needed_email` # # user.activation_needed_email_method_name = - # activation success email method on your mailer class. + # Activation success email method on your mailer class. # Default: `:activation_success_email` # # user.activation_success_email_method_name = - # do you want to prevent or allow users that did not activate by email to login? + # Do you want to prevent users who did not activate by email from logging in? # Default: `true` # # user.prevent_non_active_users_to_login = # -- reset_password -- - # reset password code attribute name. + # Password reset token attribute name. # Default: `:reset_password_token` # # user.reset_password_token_attribute_name = - # expires at attribute name. + # Password token expiry attribute name. # Default: `:reset_password_token_expires_at` # # user.reset_password_token_expires_at_attribute_name = - # when was email sent, used for hammering protection. + # When was password reset email sent. Used for hammering protection. # Default: `:reset_password_email_sent_at` # # user.reset_password_email_sent_at_attribute_name = - # mailer class. Needed. + # REQUIRED: + # Password reset mailer class. # Default: `nil` # # user.reset_password_mailer = - # reset password email method on your mailer class. + # Reset password email method on your mailer class. # Default: `:reset_password_email` # # user.reset_password_email_method_name = - # when true sorcery will not automatically - # email password reset details and allow you to - # manually handle how and when email is sent + # When true, sorcery will not automatically + # send the password reset details email, and allow you to + # manually handle how and when the email is sent # Default: `false` # # user.reset_password_mailer_disabled = - # how many seconds before the reset request expires. nil for never expires. + # How many seconds before the reset request expires. nil for never expires. # Default: `nil` # # user.reset_password_expiration_period = - # hammering protection, how long in seconds to wait before allowing another email to be sent. + # Hammering protection: how long in seconds to wait before allowing another email to be sent. # Default: `5 * 60` # # user.reset_password_time_between_emails = - - # access counter to a reset password page attribute name + + # Access counter to a reset password page attribute name # Default: `:access_count_to_reset_password_page` # # user.reset_password_page_access_count_attribute_name = # -- magic_login -- - # magic login code attribute name. + # Magic login code attribute name. # Default: `:magic_login_token` # # user.magic_login_token_attribute_name = - - # expires at attribute name. + # Magic login expiry attribute name. # Default: `:magic_login_token_expires_at` # # user.magic_login_token_expires_at_attribute_name = - - # when was email sent, used for hammering protection. + # When was magic login email sent — used for hammering protection. # Default: `:magic_login_email_sent_at` # # user.magic_login_email_sent_at_attribute_name = - - # mailer class. Needed. + # REQUIRED: + # Magic login mailer class. # Default: `nil` # # user.magic_login_mailer_class = - - # magic login email method on your mailer class. + # Magic login email method on your mailer class. # Default: `:magic_login_email` # # user.magic_login_email_method_name = - - # when true sorcery will not automatically - # email magic login details and allow you to - # manually handle how and when email is sent + # When true, sorcery will not automatically + # send magic login details email, and allow you to + # manually handle how and when the email is sent # Default: `true` # # user.magic_login_mailer_disabled = - - # how many seconds before the request expires. nil for never expires. + # How many seconds before the request expires. nil for never expires. # Default: `nil` # # user.magic_login_expiration_period = - - # hammering protection, how long in seconds to wait before allowing another email to be sent. + # Hammering protection: how long in seconds to wait before allowing another email to be sent. # Default: `5 * 60` # # user.magic_login_time_between_emails = @@ -451,12 +451,12 @@ # # user.lock_expires_at_attribute_name = - # How many failed logins allowed. + # How many failed logins are allowed. # Default: `50` # # user.consecutive_login_retries_amount_limit = - # How long the user should be banned. in seconds. 0 for permanent. + # How long the user should be banned, in seconds. 0 for permanent. # Default: `60 * 60` # # user.login_lock_time_period = @@ -471,16 +471,17 @@ # # user.unlock_token_email_method_name = - # when true sorcery will not automatically - # send email with unlock token + # When true, sorcery will not automatically + # send email with the unlock token # Default: `false` # # user.unlock_token_mailer_disabled = true - # Unlock token mailer class + # REQUIRED: + # Unlock token mailer class. # Default: `nil` # - # user.unlock_token_mailer = UserMailer + # user.unlock_token_mailer = # -- activity logging -- # Last login attribute name. @@ -498,7 +499,7 @@ # # user.last_activity_at_attribute_name = - # How long since last activity is the user defined logged out? + # How long since user's last activity will they be considered logged out? # Default: `10 * 60` # # user.activity_timeout = @@ -509,17 +510,17 @@ # # user.authentications_class = - # User's identifier in authentications class. + # User's identifier in the `authentications` class. # Default: `:user_id` # # user.authentications_user_id_attribute_name = - # Provider's identifier in authentications class. + # Provider's identifier in the `authentications` class. # Default: `:provider` # # user.provider_attribute_name = - # User's external unique identifier in authentications class. + # User's external unique identifier in the `authentications` class. # Default: `:uid` # # user.provider_uid_attribute_name = @@ -527,5 +528,5 @@ # This line must come after the 'user config' block. # Define which model authenticates with sorcery. - config.user_class = '<%= model_class_name %>' + config.user_class = "<%= model_class_name %>" end From fb70165012956b502409c31d9b8148eb4bcd7fc9 Mon Sep 17 00:00:00 2001 From: John Ellis Date: Sat, 16 Mar 2019 03:48:47 -0500 Subject: [PATCH 054/112] Add #change_password method to reset_password module. (#165) * Add #change_password method to reset_password module. Serves the same function as `#change_password!` but without raising exceptions on save. Useful for returning validation errors back to the view, for example, when `:password_confirmation` did not match `:password`. * Update documentation. * Rework tests per review comments. * Clarify what's happening with save methods in specs. --- README.md | 3 ++- .../model/submodules/reset_password.rb | 8 ++++++-- .../user_reset_password_shared_examples.rb | 20 +++++++++++++++++-- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0ee09911..c99830ae 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,8 @@ force_forget_me! # Forgets all sessions by clearing the token, even if remember_ User.load_from_reset_password_token(token) @user.generate_reset_password_token! # Use if you want to send the email by yourself @user.deliver_reset_password_instructions! # Generates the token and sends the email -@user.change_password!(new_password) +@user.change_password(new_password) +@user.change_password!(new_password) # Same as change_password but raises exception on save ``` ### Session Timeout diff --git a/lib/sorcery/model/submodules/reset_password.rb b/lib/sorcery/model/submodules/reset_password.rb index 48e5f312..0cbaab93 100644 --- a/lib/sorcery/model/submodules/reset_password.rb +++ b/lib/sorcery/model/submodules/reset_password.rb @@ -124,10 +124,14 @@ def reset_password_reset_page_access_counter end # Clears token and tries to update the new password for the user. - def change_password!(new_password) + def change_password(new_password, raise_on_failure: false) clear_reset_password_token send(:"#{sorcery_config.password_attribute_name}=", new_password) - sorcery_adapter.save raise_on_failure: true + sorcery_adapter.save raise_on_failure: raise_on_failure + end + + def change_password!(new_password) + change_password(new_password, raise_on_failure: true) end protected diff --git a/spec/shared_examples/user_reset_password_shared_examples.rb b/spec/shared_examples/user_reset_password_shared_examples.rb index be117034..e07e861c 100644 --- a/spec/shared_examples/user_reset_password_shared_examples.rb +++ b/spec/shared_examples/user_reset_password_shared_examples.rb @@ -14,6 +14,8 @@ context 'API' do specify { expect(user).to respond_to :deliver_reset_password_instructions! } + specify { expect(user).to respond_to :change_password } + specify { expect(user).to respond_to :change_password! } it 'responds to .load_from_reset_password_token' do @@ -314,13 +316,27 @@ end end - it 'when change_password! is called, deletes reset_password_token' do + it 'when change_password! is called, deletes reset_password_token and calls #save!' do user.deliver_reset_password_instructions! expect(user.reset_password_token).not_to be_nil + expect(user).to_not receive(:save) + expect(user).to receive(:save!) user.change_password!('blabulsdf') - user.save! + + expect(user.reset_password_token).to be_nil + end + + it 'when change_password is called, deletes reset_password_token and calls #save' do + new_password = 'blabulsdf' + + user.deliver_reset_password_instructions! + expect(user.reset_password_token).not_to be_nil + expect(user).to_not receive(:save!) + expect(user).to receive(:save) + + user.change_password(new_password) expect(user.reset_password_token).to be_nil end From ae02d2e07431daf287f055293790486ac073be6f Mon Sep 17 00:00:00 2001 From: Koji Onishi Date: Sat, 16 Mar 2019 18:01:27 +0900 Subject: [PATCH 055/112] allow BCrypt to have app-specific secret token (#173) * Introduce pepper option (currently available with BCrypt) Before this change, config option 'salt_join_token' has been ignored when BCrypt is chosen as crypto-provider (default). This patch introduces its alternative option that allows users to add app-specific secret token with BCrypt. To ensure compatibility, this change only takes place when the new option 'pepper' is provided, and does NOT affect on those who have already set salt_join_token. * fix typos and remove an unnecessary block * add test to describe behaviors with empty-string pepper --- lib/sorcery/crypto_providers/bcrypt.rb | 7 ++- lib/sorcery/model.rb | 1 + lib/sorcery/model/config.rb | 5 ++ spec/shared_examples/user_shared_examples.rb | 63 ++++++++++++++++++++ spec/sorcery_crypto_providers_spec.rb | 60 +++++++++++++++++++ 5 files changed, 135 insertions(+), 1 deletion(-) diff --git a/lib/sorcery/crypto_providers/bcrypt.rb b/lib/sorcery/crypto_providers/bcrypt.rb index 85413990..bf2b5d8c 100644 --- a/lib/sorcery/crypto_providers/bcrypt.rb +++ b/lib/sorcery/crypto_providers/bcrypt.rb @@ -40,6 +40,10 @@ module CryptoProviders # You are good to go! class BCrypt class << self + # Setting the option :pepper allows users to append an app-specific secret token. + # Basically it's equivalent to :salt_join_token option, but have a different name to ensure + # backward compatibility in generating/matching passwords. + attr_accessor :pepper # This is the :cost option for the BCrpyt library. # The higher the cost the more secure it is and the longer is take the generate a hash. By default this is 10. # Set this to whatever you want, play around with it to get that perfect balance between @@ -77,12 +81,13 @@ def cost_matches?(hash) def reset! @cost = 10 + @pepper = '' end private def join_tokens(tokens) - tokens.flatten.join + tokens.flatten.join.concat(pepper.to_s) # make sure to add pepper in case tokens have only one element end def new_from_hash(hash) diff --git a/lib/sorcery/model.rb b/lib/sorcery/model.rb index a195bdd7..f034cfa6 100644 --- a/lib/sorcery/model.rb +++ b/lib/sorcery/model.rb @@ -142,6 +142,7 @@ def authentication_response(options = {}) def set_encryption_attributes @sorcery_config.encryption_provider.stretches = @sorcery_config.stretches if @sorcery_config.encryption_provider.respond_to?(:stretches) && @sorcery_config.stretches @sorcery_config.encryption_provider.join_token = @sorcery_config.salt_join_token if @sorcery_config.encryption_provider.respond_to?(:join_token) && @sorcery_config.salt_join_token + @sorcery_config.encryption_provider.pepper = @sorcery_config.pepper if @sorcery_config.encryption_provider.respond_to?(:pepper) && @sorcery_config.pepper end def add_config_inheritance diff --git a/lib/sorcery/model/config.rb b/lib/sorcery/model/config.rb index 885c27ee..976e466d 100644 --- a/lib/sorcery/model/config.rb +++ b/lib/sorcery/model/config.rb @@ -12,7 +12,11 @@ class Config attr_accessor :downcase_username_before_authenticating # change default crypted_password attribute. attr_accessor :crypted_password_attribute_name + # application-specific secret token that is joined with the password and its salt. + # Currently available with BCrypt (default crypt provider) only. + attr_accessor :pepper # what pattern to use to join the password with the salt + # APPLICABLE TO MD5, SHA1, SHA256, SHA512. Other crypt providers (incl. BCrypt) ignore this parameter. attr_accessor :salt_join_token # change default salt attribute. attr_accessor :salt_attribute_name @@ -57,6 +61,7 @@ def initialize :@encryption_provider => CryptoProviders::BCrypt, :@custom_encryption_provider => nil, :@encryption_key => nil, + :@pepper => '', :@salt_join_token => '', :@salt_attribute_name => :salt, :@stretches => nil, diff --git a/spec/shared_examples/user_shared_examples.rb b/spec/shared_examples/user_shared_examples.rb index 89e69f6f..39324b89 100644 --- a/spec/shared_examples/user_shared_examples.rb +++ b/spec/shared_examples/user_shared_examples.rb @@ -54,6 +54,13 @@ expect(User.sorcery_config.custom_encryption_provider).to eq Array end + it "enables configuration option 'pepper'" do + pepper = '*$%&%*++' + sorcery_model_property_set(:pepper, pepper) + + expect(User.sorcery_config.pepper).to eq pepper + end + it "enables configuration option 'salt_join_token'" do salt_join_token = '--%%*&-' sorcery_model_property_set(:salt_join_token, salt_join_token) @@ -459,6 +466,14 @@ def self.matches?(crypted, *tokens) expect(User.encrypt(@text)).to eq Sorcery::CryptoProviders::SHA512.encrypt(@text) end + it 'if encryption algo is bcrypt it works' do + sorcery_model_property_set(:encryption_algorithm, :bcrypt) + + # comparison is done using BCrypt::Password#==(raw_token), not by String#== + expect(User.encrypt(@text)).to be_an_instance_of BCrypt::Password + expect(User.encrypt(@text)).to eq @text + end + it 'salt is random for each user and saved in db' do sorcery_model_property_set(:salt_attribute_name, :salt) @@ -488,6 +503,54 @@ def self.matches?(crypted, *tokens) expect(user.crypted_password).to eq Sorcery::CryptoProviders::SHA512.encrypt('secret', user.salt) end + + it 'if pepper is set uses it to encrypt' do + sorcery_model_property_set(:salt_attribute_name, :salt) + sorcery_model_property_set(:pepper, '++@^$') + sorcery_model_property_set(:encryption_algorithm, :bcrypt) + + # password comparison is done using BCrypt::Password#==(raw_token), not String#== + bcrypt_password = BCrypt::Password.new(user.crypted_password) + allow(::BCrypt::Password).to receive(:create) do |token, cost:| + # need to use common BCrypt's salt when genarating BCrypt::Password objects + # so that any generated password hashes can be compared each other + ::BCrypt::Engine.hash_secret(token, bcrypt_password.salt) + end + + expect(user.crypted_password).not_to eq Sorcery::CryptoProviders::BCrypt.encrypt('secret') + + Sorcery::CryptoProviders::BCrypt.pepper = '' + + expect(user.crypted_password).not_to eq Sorcery::CryptoProviders::BCrypt.encrypt('secret', user.salt) + + Sorcery::CryptoProviders::BCrypt.pepper = User.sorcery_config.pepper + + expect(user.crypted_password).to eq Sorcery::CryptoProviders::BCrypt.encrypt('secret', user.salt) + end + + it 'if pepper is empty string (default) does not use pepper to encrypt' do + sorcery_model_property_set(:salt_attribute_name, :salt) + sorcery_model_property_set(:pepper, '') + sorcery_model_property_set(:encryption_algorithm, :bcrypt) + + # password comparison is done using BCrypt::Password#==(raw_token), not String#== + bcrypt_password = BCrypt::Password.new(user.crypted_password) + allow(::BCrypt::Password).to receive(:create) do |token, cost:| + # need to use common BCrypt's salt when genarating BCrypt::Password objects + # so that any generated password hashes can be compared each other + ::BCrypt::Engine.hash_secret(token, bcrypt_password.salt) + end + + expect(user.crypted_password).not_to eq Sorcery::CryptoProviders::BCrypt.encrypt('secret') + + Sorcery::CryptoProviders::BCrypt.pepper = 'some_pepper' + + expect(user.crypted_password).not_to eq Sorcery::CryptoProviders::BCrypt.encrypt('secret', user.salt) + + Sorcery::CryptoProviders::BCrypt.pepper = User.sorcery_config.pepper + + expect(user.crypted_password).to eq Sorcery::CryptoProviders::BCrypt.encrypt('secret', user.salt) + end end describe 'ORM adapter' do diff --git a/spec/sorcery_crypto_providers_spec.rb b/spec/sorcery_crypto_providers_spec.rb index 44610515..796c8ac0 100644 --- a/spec/sorcery_crypto_providers_spec.rb +++ b/spec/sorcery_crypto_providers_spec.rb @@ -148,6 +148,7 @@ before(:all) do Sorcery::CryptoProviders::BCrypt.cost = 1 @digest = BCrypt::Password.create('Noam Ben-Ari', cost: Sorcery::CryptoProviders::BCrypt.cost) + @tokens = %w[password gq18WBnJYNh2arkC1kgH] end after(:each) do @@ -181,5 +182,64 @@ # stubbed in Sorcery::TestHelpers::Internal expect(Sorcery::CryptoProviders::BCrypt.cost).to eq 1 end + + it 'matches token encrypted with salt from upstream' do + # note: actual comparison is done by BCrypt::Password#==(raw_token) + expect(Sorcery::CryptoProviders::BCrypt.encrypt(@tokens)).to eq @tokens.flatten.join + end + + it 'respond_to?(:pepper) returns true' do + expect(Sorcery::CryptoProviders::BCrypt.respond_to?(:pepper)).to be true + end + + context 'when pepper is provided' do + before(:each) do + Sorcery::CryptoProviders::BCrypt.pepper = 'pepper' + @digest = Sorcery::CryptoProviders::BCrypt.encrypt(@tokens) # a BCrypt::Password object + end + + it 'matches token encrypted with salt and pepper from upstream' do + # note: actual comparison is done by BCrypt::Password#==(raw_token) + expect(@digest).to eq @tokens.flatten.join.concat('pepper') + end + + it 'matches? returns true when matches' do + expect(Sorcery::CryptoProviders::BCrypt.matches?(@digest, *@tokens)).to be true + end + + it 'matches? returns false when pepper is replaced with empty string' do + Sorcery::CryptoProviders::BCrypt.pepper = '' + expect(Sorcery::CryptoProviders::BCrypt.matches?(@digest, *@tokens)).to be false + end + + it 'matches? returns false when no match' do + expect(Sorcery::CryptoProviders::BCrypt.matches?(@digest, 'a_random_incorrect_password')).to be false + end + end + + context "when pepper is an empty string (default)" do + before(:each) do + Sorcery::CryptoProviders::BCrypt.pepper = '' + @digest = Sorcery::CryptoProviders::BCrypt.encrypt(@tokens) # a BCrypt::Password object + end + + # make sure the default pepper '' does nothing + it 'matches token encrypted with salt only (without pepper)' do + expect(@digest).to eq @tokens.flatten.join # keep consistency with the older versions of #join_token + end + + it 'matches? returns true when matches' do + expect(Sorcery::CryptoProviders::BCrypt.matches?(@digest, *@tokens)).to be true + end + + it 'matches? returns false when pepper has changed' do + Sorcery::CryptoProviders::BCrypt.pepper = 'a new pepper' + expect(Sorcery::CryptoProviders::BCrypt.matches?(@digest, *@tokens)).to be false + end + + it 'matches? returns false when no match' do + expect(Sorcery::CryptoProviders::BCrypt.matches?(@digest, 'a_random_incorrect_password')).to be false + end + end end end From ae4141e7059fa5c79d4135e81efb839a016d39ac Mon Sep 17 00:00:00 2001 From: sho ishihara Date: Wed, 10 Apr 2019 02:46:40 +0900 Subject: [PATCH 056/112] Support the LINE login auth (#80) * line provider * require line provider * spec * fix response params * set state * fix spec --- .../sorcery/templates/initializer.rb | 6 ++- lib/sorcery/controller/submodules/external.rb | 1 + lib/sorcery/providers/line.rb | 47 +++++++++++++++++++ spec/controllers/controller_oauth2_spec.rb | 8 ++++ .../app/controllers/sorcery_controller.rb | 20 ++++++++ spec/rails_app/config/routes.rb | 3 ++ 6 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 lib/sorcery/providers/line.rb diff --git a/lib/generators/sorcery/templates/initializer.rb b/lib/generators/sorcery/templates/initializer.rb index b5be6ee2..15f077b7 100644 --- a/lib/generators/sorcery/templates/initializer.rb +++ b/lib/generators/sorcery/templates/initializer.rb @@ -77,7 +77,7 @@ # -- external -- # What providers are supported by this app - # i.e. [:twitter, :facebook, :github, :linkedin, :xing, :google, :liveid, :salesforce, :slack]. + # i.e. [:twitter, :facebook, :github, :linkedin, :xing, :google, :liveid, :salesforce, :slack, :line]. # Default: `[]` # # config.external_providers = @@ -216,6 +216,10 @@ # config.salesforce.scope = "full" # config.salesforce.user_info_mapping = {:email => "email"} + # config.line.key = "" + # config.line.secret = "" + # config.line.callback_url = "http://mydomain.com:3000/oauth/callback?provider=line" + # --- user config --- config.user_config do |user| # -- core -- diff --git a/lib/sorcery/controller/submodules/external.rb b/lib/sorcery/controller/submodules/external.rb index e48ff4cb..907c3cf0 100644 --- a/lib/sorcery/controller/submodules/external.rb +++ b/lib/sorcery/controller/submodules/external.rb @@ -25,6 +25,7 @@ def self.included(base) require 'sorcery/providers/microsoft' require 'sorcery/providers/instagram' require 'sorcery/providers/auth0' + require 'sorcery/providers/line' Config.module_eval do class << self diff --git a/lib/sorcery/providers/line.rb b/lib/sorcery/providers/line.rb new file mode 100644 index 00000000..b903490e --- /dev/null +++ b/lib/sorcery/providers/line.rb @@ -0,0 +1,47 @@ +module Sorcery + module Providers + # This class adds support for OAuth with line.com. + # + # config.line.key = + # config.line.secret = + # ... + # + class Line < Base + include Protocols::Oauth2 + + attr_accessor :token_url, :user_info_path, :auth_path + + def initialize + super + + @site = 'https://access.line.me' + @user_info_path = 'https://api.line.me/v2/profile' + @token_url = 'https://api.line.me/v2/oauth/accessToken' + @auth_path = 'dialog/oauth/weblogin' + end + + def get_user_hash(access_token) + response = access_token.get(user_info_path) + auth_hash(access_token).tap do |h| + h[:user_info] = JSON.parse(response.body) + h[:uid] = h[:user_info]['userId'].to_s + end + end + + # calculates and returns the url to which the user should be redirected, + # to get authenticated at the external provider's site. + def login_url(_params, _session) + @state = SecureRandom.hex(16) + authorize_url(authorize_url: auth_path) + end + # tries to login the user from access token + def process_callback(params, _session) + args = {}.tap do |a| + a[:code] = params[:code] if params[:code] + end + + get_access_token(args, token_url: token_url, token_method: :post) + end + end + end +end diff --git a/spec/controllers/controller_oauth2_spec.rb b/spec/controllers/controller_oauth2_spec.rb index eb159009..6d394729 100644 --- a/spec/controllers/controller_oauth2_spec.rb +++ b/spec/controllers/controller_oauth2_spec.rb @@ -216,6 +216,7 @@ microsoft instagram auth0 + line ] ) @@ -257,6 +258,9 @@ sorcery_controller_external_property_set(:auth0, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') sorcery_controller_external_property_set(:auth0, :callback_url, 'http://blabla.com') sorcery_controller_external_property_set(:auth0, :site, 'https://sorcery-test.auth0.com') + sorcery_controller_external_property_set(:line, :key, "eYVNBjBDi33aa9GkA3w") + sorcery_controller_external_property_set(:line, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") + sorcery_controller_external_property_set(:line, :callback_url, "http://blabla.com") end after(:each) do @@ -474,6 +478,7 @@ def set_external_property microsoft instagram auth0 + line ] ) sorcery_controller_external_property_set(:facebook, :key, 'eYVNBjBDi33aa9GkA3w') @@ -513,6 +518,9 @@ def set_external_property sorcery_controller_external_property_set(:auth0, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') sorcery_controller_external_property_set(:auth0, :callback_url, 'http://blabla.com') sorcery_controller_external_property_set(:auth0, :site, 'https://sorcery-test.auth0.com') + sorcery_controller_external_property_set(:line, :key, "eYVNBjBDi33aa9GkA3w") + sorcery_controller_external_property_set(:line, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") + sorcery_controller_external_property_set(:line, :callback_url, "http://blabla.com") end def provider_url(provider) diff --git a/spec/rails_app/app/controllers/sorcery_controller.rb b/spec/rails_app/app/controllers/sorcery_controller.rb index 21b15f69..a698ce3e 100644 --- a/spec/rails_app/app/controllers/sorcery_controller.rb +++ b/spec/rails_app/app/controllers/sorcery_controller.rb @@ -142,6 +142,10 @@ def login_at_test_slack login_at(:slack) end + def login_at_test_line + login_at(:line) + end + def login_at_test_with_state login_at(:facebook, state: 'bla') end @@ -268,6 +272,14 @@ def test_login_from_auth0 end end + def test_login_from_line + if @user = login_from(:line) + redirect_to 'bla', notice: 'Success!' + else + redirect_to 'blu', alert: 'Failed!' + end + end + def test_return_to_with_external_twitter if (@user = login_from(:twitter)) redirect_back_or_to 'bla', notice: 'Success!' @@ -382,6 +394,14 @@ def test_return_to_with_external_auth0 end end + def test_return_to_with_external_line + if @user = login_from(:line) + redirect_back_or_to 'bla', notice: 'Success!' + else + redirect_to 'blu', alert: 'Failed!' + end + end + def test_create_from_provider provider = params[:provider] login_from(provider) diff --git a/spec/rails_app/config/routes.rb b/spec/rails_app/config/routes.rb index 03d4eb2a..66dbb240 100644 --- a/spec/rails_app/config/routes.rb +++ b/spec/rails_app/config/routes.rb @@ -32,6 +32,7 @@ get :test_login_from_slack get :test_login_from_instagram get :test_login_from_auth0 + get :test_login_from_line get :login_at_test get :login_at_test_twitter get :login_at_test_facebook @@ -47,6 +48,7 @@ get :login_at_test_slack get :login_at_test_instagram get :login_at_test_auth0 + get :login_at_test_line get :test_return_to_with_external get :test_return_to_with_external_twitter get :test_return_to_with_external_facebook @@ -62,6 +64,7 @@ get :test_return_to_with_external_slack get :test_return_to_with_external_instagram get :test_return_to_with_external_auth0 + get :test_return_to_with_external_line get :test_http_basic_auth get :some_action_making_a_non_persisted_change_to_the_user post :test_login_with_remember From cd6cffd0e0181162bf8b968261e76eca9e772c6c Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Wed, 22 May 2019 00:04:57 +0000 Subject: [PATCH 057/112] Update LinkedIn to use OAuth 2 (#189) * Update changelog * Update LinkedIn provider to use OAuth2 per v1 deprecation * Update changelog --- CHANGELOG.md | 9 +++ .../sorcery/templates/initializer.rb | 7 +-- lib/sorcery/providers/linkedin.rb | 56 +++++++------------ 3 files changed, 30 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b259df49..514b0bec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ # Changelog ## HEAD +* Update LinkedIn to use OAuth 2 [#189](https://github.com/Sorcery/sorcery/pull/189) +* Support the LINE login auth [#80](https://github.com/Sorcery/sorcery/pull/80) +* Allow BCrypt to have app-specific secret token [#173](https://github.com/Sorcery/sorcery/pull/173) +* Add #change_password method to reset_password module. [#165](https://github.com/Sorcery/sorcery/pull/165) +* Clean up initializer comments [#153](https://github.com/Sorcery/sorcery/pull/153) +* Allow load_from_magic_login_token to accept a block [#152](https://github.com/Sorcery/sorcery/pull/152) +* Fix CipherError class name [#142](https://github.com/Sorcery/sorcery/pull/142) +* Fix `update_failed_logins_count` being called twice when login failed [#163](https://github.com/Sorcery/sorcery/pull/163) +* Update migration templates to use new hash syntax [#170](https://github.com/Sorcery/sorcery/pull/170) * Support for Rails 4.2 and lower soft-dropped [#171](https://github.com/Sorcery/sorcery/pull/171) ## 0.13.0 diff --git a/lib/generators/sorcery/templates/initializer.rb b/lib/generators/sorcery/templates/initializer.rb index 15f077b7..6a08466b 100644 --- a/lib/generators/sorcery/templates/initializer.rb +++ b/lib/generators/sorcery/templates/initializer.rb @@ -88,16 +88,11 @@ # # config.ca_file = - # For information about LinkedIn API: - # - user info fields go to https://developer.linkedin.com/documents/profile-fields - # - access permissions go to https://developer.linkedin.com/documents/authentication#granting - # # config.linkedin.key = "" # config.linkedin.secret = "" # config.linkedin.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=linkedin" - # config.linkedin.user_info_fields = ['first-name', 'last-name'] # config.linkedin.user_info_mapping = {first_name: "firstName", last_name: "lastName"} - # config.linkedin.access_permissions = ['r_basicprofile'] + # config.linkedin.scope = "r_basicprofile" # # # For information about XING API: diff --git a/lib/sorcery/providers/linkedin.rb b/lib/sorcery/providers/linkedin.rb index 7404a9fe..959c8a65 100644 --- a/lib/sorcery/providers/linkedin.rb +++ b/lib/sorcery/providers/linkedin.rb @@ -1,65 +1,49 @@ module Sorcery module Providers - # This class adds support for OAuth with Linkedin.com. + # This class adds support for OAuth with LinkedIn. # # config.linkedin.key = # config.linkedin.secret = # ... # class Linkedin < Base - include Protocols::Oauth + include Protocols::Oauth2 - attr_accessor :authorize_path, :access_permissions, :access_token_path, - :request_token_path, :user_info_fields, :user_info_path + attr_accessor :auth_url, :scope, :token_url, :user_info_url def initialize - @configuration = { - site: 'https://api.linkedin.com', - authorize_path: '/uas/oauth/authenticate', - request_token_path: '/uas/oauth/requestToken', - access_token_path: '/uas/oauth/accessToken' - } - @user_info_path = '/v1/people/~' - end - - # Override included get_consumer method to provide authorize_path - def get_consumer - # Add access permissions to request token path - @configuration[:request_token_path] += '?scope=' + access_permissions.join('+') unless access_permissions.blank? || @configuration[:request_token_path].include?('?scope=') - ::OAuth::Consumer.new(@key, @secret, @configuration) + super + + @site = 'https://api.linkedin.com' + @auth_url = '/oauth/v2/authorization' + @token_url = '/oauth/v2/accessToken' + @user_info_url = 'https://api.linkedin.com/v2/me' + @scope = 'r_liteprofile' + @state = SecureRandom.hex(16) end def get_user_hash(access_token) - # Always include id for provider uid and prevent accidental duplication via setting `user_info_field = ['id']` (needed in Sorcery 0.9.1) - info_fields = user_info_fields ? user_info_fields.reject { |n| n == 'id' } : [] - fields = info_fields.any? ? 'id,' + info_fields.join(',') : 'id' - response = access_token.get("#{@user_info_path}:(#{fields})", 'x-li-format' => 'json') + response = access_token.get(user_info_url) auth_hash(access_token).tap do |h| h[:user_info] = JSON.parse(response.body) - h[:uid] = h[:user_info]['id'].to_s + h[:uid] = h[:user_info]['id'] end end # calculates and returns the url to which the user should be redirected, # to get authenticated at the external provider's site. - def login_url(_params, session) - req_token = get_request_token - session[:request_token] = req_token.token - session[:request_token_secret] = req_token.secret - authorize_url(request_token: req_token.token, request_token_secret: req_token.secret) + def login_url(_params, _session) + authorize_url(authorize_url: auth_url) end # tries to login the user from access token - def process_callback(params, session) - args = { - oauth_verifier: params[:oauth_verifier], - request_token: session[:request_token], - request_token_secret: session[:request_token_secret] - } + def process_callback(params, _session) + args = {}.tap do |a| + a[:code] = params[:code] if params[:code] + end - args[:code] = params[:code] if params[:code] - get_access_token(args) + get_access_token(args, token_url: token_url, token_method: :post) end end end From 26ad5a82e3b0d32773ac74f051a52aee62c4aedc Mon Sep 17 00:00:00 2001 From: Joshua Buker Date: Tue, 21 May 2019 17:07:12 -0700 Subject: [PATCH 058/112] Release 0.14.0 --- CHANGELOG.md | 2 ++ lib/sorcery/version.rb | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 514b0bec..bd564d86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## HEAD +## 0.14.0 + * Update LinkedIn to use OAuth 2 [#189](https://github.com/Sorcery/sorcery/pull/189) * Support the LINE login auth [#80](https://github.com/Sorcery/sorcery/pull/80) * Allow BCrypt to have app-specific secret token [#173](https://github.com/Sorcery/sorcery/pull/173) diff --git a/lib/sorcery/version.rb b/lib/sorcery/version.rb index 74a652ad..5d85c098 100644 --- a/lib/sorcery/version.rb +++ b/lib/sorcery/version.rb @@ -1,3 +1,3 @@ module Sorcery - VERSION = '0.13.0'.freeze + VERSION = '0.14.0'.freeze end From e6b78f1a3a6feccbbd20063cb1a97d2a211810c2 Mon Sep 17 00:00:00 2001 From: mvandenbeuken Date: Tue, 28 May 2019 12:47:22 -0600 Subject: [PATCH 059/112] Allow for custom providers with multi-word class names. (#190) * Allow for custom providers with multi-word class names. Currently sorcery will not handle custom providers with names like: ExampleProvider. config.example_provider would search for a class called Example_provider rather than ExampleProvider. Using .classify rather than .capitalize will fix this. I've added an example provider implementation under the spec/support directory, and have a test that ensures that it is loaded properly via config. The existing tests for existing providers are also coming back with successes. * Add spec to cover single-word custom providers --- lib/sorcery/controller/submodules/external.rb | 2 +- spec/providers/example_provider_spec.rb | 17 +++++++++++++++++ spec/providers/example_spec.rb | 17 +++++++++++++++++ spec/support/providers/example.rb | 11 +++++++++++ spec/support/providers/example_provider.rb | 11 +++++++++++ 5 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 spec/providers/example_provider_spec.rb create mode 100644 spec/providers/example_spec.rb create mode 100644 spec/support/providers/example.rb create mode 100644 spec/support/providers/example_provider.rb diff --git a/lib/sorcery/controller/submodules/external.rb b/lib/sorcery/controller/submodules/external.rb index 907c3cf0..dcaacad4 100644 --- a/lib/sorcery/controller/submodules/external.rb +++ b/lib/sorcery/controller/submodules/external.rb @@ -38,7 +38,7 @@ def external_providers=(providers) providers.each do |name| class_eval <<-RUBY, __FILE__, __LINE__ + 1 def self.#{name} - @#{name} ||= Sorcery::Providers.const_get('#{name}'.to_s.capitalize).new + @#{name} ||= Sorcery::Providers.const_get('#{name}'.to_s.classify).new end RUBY end diff --git a/spec/providers/example_provider_spec.rb b/spec/providers/example_provider_spec.rb new file mode 100644 index 00000000..c8317f98 --- /dev/null +++ b/spec/providers/example_provider_spec.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'sorcery/providers/base' + +describe Sorcery::Providers::ExampleProvider do + before(:all) do + sorcery_reload!([:external]) + sorcery_controller_property_set(:external_providers, [:example_provider]) + end + + context 'fetching a multi-word custom provider' do + it 'returns the provider' do + expect(Sorcery::Controller::Config.example_provider).to be_a(Sorcery::Providers::ExampleProvider) + end + end +end diff --git a/spec/providers/example_spec.rb b/spec/providers/example_spec.rb new file mode 100644 index 00000000..a022695c --- /dev/null +++ b/spec/providers/example_spec.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'sorcery/providers/base' + +describe Sorcery::Providers::Example do + before(:all) do + sorcery_reload!([:external]) + sorcery_controller_property_set(:external_providers, [:example]) + end + + context 'fetching a single-word custom provider' do + it 'returns the provider' do + expect(Sorcery::Controller::Config.example).to be_a(Sorcery::Providers::Example) + end + end +end diff --git a/spec/support/providers/example.rb b/spec/support/providers/example.rb new file mode 100644 index 00000000..e110789e --- /dev/null +++ b/spec/support/providers/example.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'sorcery/providers/base' + +module Sorcery + module Providers + class Example < Base + include Protocols::Oauth2 + end + end +end diff --git a/spec/support/providers/example_provider.rb b/spec/support/providers/example_provider.rb new file mode 100644 index 00000000..6428ad6a --- /dev/null +++ b/spec/support/providers/example_provider.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'sorcery/providers/base' + +module Sorcery + module Providers + class ExampleProvider < Base + include Protocols::Oauth2 + end + end +end From f67b9895a5816c188e40c16d2ab72e1bc8d94134 Mon Sep 17 00:00:00 2001 From: Ryusuke Tomita Date: Wed, 29 May 2019 03:49:07 +0900 Subject: [PATCH 060/112] ignore cookies when undefined cookies (#187) --- lib/sorcery/controller/submodules/remember_me.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sorcery/controller/submodules/remember_me.rb b/lib/sorcery/controller/submodules/remember_me.rb index fa93f4e9..57af975d 100644 --- a/lib/sorcery/controller/submodules/remember_me.rb +++ b/lib/sorcery/controller/submodules/remember_me.rb @@ -59,7 +59,7 @@ def auto_login(user, should_remember = false) # and logs the user in if found. # Runs as a login source. See 'current_user' method for how it is used. def login_from_cookie - user = cookies.signed[:remember_me_token] && user_class.sorcery_adapter.find_by_remember_me_token(cookies.signed[:remember_me_token]) + user = cookies.signed[:remember_me_token] && user_class.sorcery_adapter.find_by_remember_me_token(cookies.signed[:remember_me_token]) if defined? cookies if user && user.has_remember_me_token? set_remember_me_cookie!(user) session[:user_id] = user.id.to_s From 4bda5a842d131ab5a1a888d02fe3e119cf1d204f Mon Sep 17 00:00:00 2001 From: JakubSzajna Date: Mon, 17 Jun 2019 01:25:52 +0200 Subject: [PATCH 061/112] Fix email scope for LinkedIn Provider (#191) * Used user email address from LinkedIn * Update example user_info_mapping in initializer * Force r_emailaddress in @scope in linkedin provider * Load email only if r_emailaddress is in scope * Adjust initializers Linkedin comment --- .../sorcery/templates/initializer.rb | 12 ++++- lib/sorcery/providers/linkedin.rb | 45 ++++++++++++++----- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/lib/generators/sorcery/templates/initializer.rb b/lib/generators/sorcery/templates/initializer.rb index 6a08466b..22e0f441 100644 --- a/lib/generators/sorcery/templates/initializer.rb +++ b/lib/generators/sorcery/templates/initializer.rb @@ -88,11 +88,19 @@ # # config.ca_file = + # Linkedin requires r_emailaddress scope to fetch user's email address. + # You can skip including the email field if you use an intermediary signup form. (using build_from method). + # The r_emailaddress scope is only necessary if you are using the create_from method directly. + # # config.linkedin.key = "" # config.linkedin.secret = "" # config.linkedin.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=linkedin" - # config.linkedin.user_info_mapping = {first_name: "firstName", last_name: "lastName"} - # config.linkedin.scope = "r_basicprofile" + # config.linkedin.user_info_mapping = { + # first_name: 'localizedFirstName', + # last_name: 'localizedLastName', + # email: 'emailAddress' + # } + # config.linkedin.scope = "r_liteprofile r_emailaddress" # # # For information about XING API: diff --git a/lib/sorcery/providers/linkedin.rb b/lib/sorcery/providers/linkedin.rb index 959c8a65..f4545323 100644 --- a/lib/sorcery/providers/linkedin.rb +++ b/lib/sorcery/providers/linkedin.rb @@ -9,25 +9,26 @@ module Providers class Linkedin < Base include Protocols::Oauth2 - attr_accessor :auth_url, :scope, :token_url, :user_info_url + attr_accessor :auth_url, :scope, :token_url, :user_info_url, :email_info_url def initialize super - @site = 'https://api.linkedin.com' - @auth_url = '/oauth/v2/authorization' - @token_url = '/oauth/v2/accessToken' - @user_info_url = 'https://api.linkedin.com/v2/me' - @scope = 'r_liteprofile' - @state = SecureRandom.hex(16) + @site = 'https://api.linkedin.com' + @auth_url = '/oauth/v2/authorization' + @token_url = '/oauth/v2/accessToken' + @user_info_url = 'https://api.linkedin.com/v2/me' + @email_info_url = 'https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))' + @scope = 'r_liteprofile r_emailaddress' + @state = SecureRandom.hex(16) end def get_user_hash(access_token) - response = access_token.get(user_info_url) + user_info = get_user_info(access_token) auth_hash(access_token).tap do |h| - h[:user_info] = JSON.parse(response.body) - h[:uid] = h[:user_info]['id'] + h[:user_info] = user_info + h[:uid] = h[:user_info]['id'] end end @@ -45,6 +46,30 @@ def process_callback(params, _session) get_access_token(args, token_url: token_url, token_method: :post) end + + def get_user_info(access_token) + response = access_token.get(user_info_url) + user_info = JSON.parse(response.body) + + if email_in_scope? + email = fetch_email(access_token) + + return user_info.merge(email) + end + + user_info + end + + def email_in_scope? + scope.include?('r_emailaddress') + end + + def fetch_email(access_token) + email_response = access_token.get(email_info_url) + email_info = JSON.parse(email_response.body)['elements'].first + + email_info['handle~'] + end end end end From 5c7933c924041d6501a6917e952cc9c949e3ae0f Mon Sep 17 00:00:00 2001 From: Kevin Lawrence <36310506+kevin-smartpatients@users.noreply.github.com> Date: Tue, 2 Jul 2019 17:25:42 +0100 Subject: [PATCH 062/112] Don't :return_to JSON requests after login. (#197) (previous code skipped XHR requests, but not all JSON requests are XHR) --- lib/sorcery/controller.rb | 5 ++++- spec/controllers/controller_spec.rb | 10 ++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/sorcery/controller.rb b/lib/sorcery/controller.rb index 7b40adce..ba4a69fd 100644 --- a/lib/sorcery/controller.rb +++ b/lib/sorcery/controller.rb @@ -25,7 +25,10 @@ module InstanceMethods def require_login return if logged_in? - session[:return_to_url] = request.url if Config.save_return_to_url && request.get? && !request.xhr? + if Config.save_return_to_url && request.get? && !request.xhr? && !request.format.json? + session[:return_to_url] = request.url + end + send(Config.not_authenticated_action) end diff --git a/spec/controllers/controller_spec.rb b/spec/controllers/controller_spec.rb index 83e00a21..cd1e4e89 100644 --- a/spec/controllers/controller_spec.rb +++ b/spec/controllers/controller_spec.rb @@ -150,6 +150,16 @@ end end + it 'require_login before_action does not save the url for JSON requests' do + get :some_action, format: :json + expect(session[:return_to_url]).to be_nil + end + + it 'require_login before_action does not save the url for XHR requests' do + get :some_action, xhr: true + expect(session[:return_to_url]).to be_nil + end + it 'on successful login the user is redirected to the url he originally wanted' do session[:return_to_url] = 'http://test.host/some_action' post :test_return_to, params: { email: 'bla@bla.com', password: 'secret' } From 8d2d0a59bdeeb207428f9ba3073571f563ed01e6 Mon Sep 17 00:00:00 2001 From: Alexey Naumov Date: Mon, 16 Sep 2019 19:19:25 +0300 Subject: [PATCH 063/112] Use id instead of uid for VK provider (#199) * Use id instead of uid for VK provider * fix specs --- lib/sorcery/providers/vk.rb | 2 +- spec/controllers/controller_oauth2_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/sorcery/providers/vk.rb b/lib/sorcery/providers/vk.rb index ebbaf636..16896c54 100644 --- a/lib/sorcery/providers/vk.rb +++ b/lib/sorcery/providers/vk.rb @@ -37,7 +37,7 @@ def get_user_hash(access_token) user_hash[:user_info] = user_hash[:user_info]['response'][0] user_hash[:user_info]['full_name'] = [user_hash[:user_info]['first_name'], user_hash[:user_info]['last_name']].join(' ') - user_hash[:uid] = user_hash[:user_info]['uid'] + user_hash[:uid] = user_hash[:user_info]['id'] user_hash[:user_info]['email'] = access_token.params['email'] end user_hash diff --git a/spec/controllers/controller_oauth2_spec.rb b/spec/controllers/controller_oauth2_spec.rb index 6d394729..46e54127 100644 --- a/spec/controllers/controller_oauth2_spec.rb +++ b/spec/controllers/controller_oauth2_spec.rb @@ -427,7 +427,7 @@ def stub_all_oauth2_requests! # response for VK auth 'response' => [ { - 'uid' => '123', + 'id' => '123', 'first_name' => 'Noam', 'last_name' => 'Ben Ari' } From deb8a463222ef31a7a4cf2f03d9641c196479307 Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Wed, 27 Nov 2019 19:26:32 +0000 Subject: [PATCH 064/112] Remove MySQL database creation call (#214) * Remove MySQL database creation call * Add missing manifest.js file * Wrong config folder... --- .travis.yml | 3 --- spec/rails_app/app/assets/config/manifest.js | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) create mode 100644 spec/rails_app/app/assets/config/manifest.js diff --git a/.travis.yml b/.travis.yml index a6facab1..6904be93 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,3 @@ rvm: gemfile: - Gemfile - -before_script: - - mysql -e 'create database sorcery_test;' diff --git a/spec/rails_app/app/assets/config/manifest.js b/spec/rails_app/app/assets/config/manifest.js new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/spec/rails_app/app/assets/config/manifest.js @@ -0,0 +1 @@ +{} From dd03140738d7f491804a56447947b6a0407ae6d3 Mon Sep 17 00:00:00 2001 From: Tomoyuki Tanaka Date: Tue, 10 Dec 2019 08:57:26 +0900 Subject: [PATCH 065/112] Add discord provider (#185) * add discord provider * add initializer comment * add discord spec --- .../sorcery/templates/initializer.rb | 6 +++ lib/sorcery/controller/submodules/external.rb | 1 + lib/sorcery/providers/discord.rb | 52 +++++++++++++++++++ spec/controllers/controller_oauth2_spec.rb | 19 +++++-- .../app/controllers/sorcery_controller.rb | 20 +++++++ spec/rails_app/config/routes.rb | 3 ++ 6 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 lib/sorcery/providers/discord.rb diff --git a/lib/generators/sorcery/templates/initializer.rb b/lib/generators/sorcery/templates/initializer.rb index 22e0f441..9b7829de 100644 --- a/lib/generators/sorcery/templates/initializer.rb +++ b/lib/generators/sorcery/templates/initializer.rb @@ -223,6 +223,12 @@ # config.line.secret = "" # config.line.callback_url = "http://mydomain.com:3000/oauth/callback?provider=line" + # For infromation about Discord API + # https://discordapp.com/developers/docs/topics/oauth2 + # config.discord.key = "xxxxxx" + # config.discord.secret = "xxxxxx" + # config.discord.callback_url = "http://localhost:3000/oauth/callback?provider=discord" + # config.discord.scope = "email guilds" # --- user config --- config.user_config do |user| # -- core -- diff --git a/lib/sorcery/controller/submodules/external.rb b/lib/sorcery/controller/submodules/external.rb index dcaacad4..07e755d2 100644 --- a/lib/sorcery/controller/submodules/external.rb +++ b/lib/sorcery/controller/submodules/external.rb @@ -26,6 +26,7 @@ def self.included(base) require 'sorcery/providers/instagram' require 'sorcery/providers/auth0' require 'sorcery/providers/line' + require 'sorcery/providers/discord' Config.module_eval do class << self diff --git a/lib/sorcery/providers/discord.rb b/lib/sorcery/providers/discord.rb new file mode 100644 index 00000000..8c9bf5c4 --- /dev/null +++ b/lib/sorcery/providers/discord.rb @@ -0,0 +1,52 @@ +module Sorcery + module Providers + # This class adds support for OAuth with discordapp.com + + class Discord < Base + include Protocols::Oauth2 + + attr_accessor :auth_path, :scope, :token_url, :user_info_path + + def initialize + super + + @scope = 'identify' + @site = 'https://discordapp.com/' + @auth_path = '/api/oauth2/authorize' + @token_url = '/api/oauth2/token' + @user_info_path = '/api/users/@me' + @state = SecureRandom.hex(16) + end + + def get_user_hash(access_token) + response = access_token.get(user_info_path) + body = JSON.parse(response.body) + auth_hash(access_token).tap do |h| + h[:user_info] = body + h[:uid] = body['id'] + end + end + + # calculates and returns the url to which the user should be redirected, + # to get authenticated at the external provider's site. + def login_url(_params, _session) + authorize_url(authorize_url: auth_path) + end + + # tries to login the user from access token + def process_callback(params, _session) + args = {}.tap do |a| + a[:code] = params[:code] if params[:code] + end + get_access_token( + args, + token_url: token_url, + client_id: @key, + client_secret: @secret, + grant_type: 'authorization_code', + token_method: :post + ) + end + end + end +end diff --git a/spec/controllers/controller_oauth2_spec.rb b/spec/controllers/controller_oauth2_spec.rb index 46e54127..2c849eaf 100644 --- a/spec/controllers/controller_oauth2_spec.rb +++ b/spec/controllers/controller_oauth2_spec.rb @@ -155,7 +155,7 @@ expect(flash[:notice]).to eq 'Success!' end - %i[github google liveid vk salesforce paypal slack wechat microsoft instagram auth0].each do |provider| + %i[github google liveid vk salesforce paypal slack wechat microsoft instagram auth0 discord].each do |provider| describe "with #{provider}" do it 'login_at redirects correctly' do get :"login_at_test_#{provider}" @@ -217,6 +217,7 @@ instagram auth0 line + discord ] ) @@ -261,6 +262,9 @@ sorcery_controller_external_property_set(:line, :key, "eYVNBjBDi33aa9GkA3w") sorcery_controller_external_property_set(:line, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") sorcery_controller_external_property_set(:line, :callback_url, "http://blabla.com") + sorcery_controller_external_property_set(:discord, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:discord, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:discord, :callback_url, 'http://blabla.com') end after(:each) do @@ -283,7 +287,7 @@ expect(ActionMailer::Base.deliveries.size).to eq old_size end - %i[github google liveid vk salesforce paypal wechat microsoft instagram auth0].each do |provider| + %i[github google liveid vk salesforce paypal wechat microsoft instagram auth0 discord].each do |provider| it "does not send activation email to external users (#{provider})" do old_size = ActionMailer::Base.deliveries.size create_new_external_user provider @@ -307,7 +311,7 @@ sorcery_reload!(%i[activity_logging external]) end - %w[facebook github google liveid vk salesforce slack].each do |provider| + %w[facebook github google liveid vk salesforce slack discord].each do |provider| context "when #{provider}" do before(:each) do sorcery_controller_property_set(:register_login_time, true) @@ -346,7 +350,7 @@ let(:user) { double('user', id: 42) } - %w[facebook github google liveid vk salesforce slack].each do |provider| + %w[facebook github google liveid vk salesforce slack discord].each do |provider| context "when #{provider}" do before(:each) do sorcery_model_property_set(:authentications_class, Authentication) @@ -479,6 +483,7 @@ def set_external_property instagram auth0 line + discord ] ) sorcery_controller_external_property_set(:facebook, :key, 'eYVNBjBDi33aa9GkA3w') @@ -521,6 +526,9 @@ def set_external_property sorcery_controller_external_property_set(:line, :key, "eYVNBjBDi33aa9GkA3w") sorcery_controller_external_property_set(:line, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") sorcery_controller_external_property_set(:line, :callback_url, "http://blabla.com") + sorcery_controller_external_property_set(:discord, :key, 'eYVNBjBDi33aa9GkA3w') + sorcery_controller_external_property_set(:discord, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') + sorcery_controller_external_property_set(:discord, :callback_url, 'http://blabla.com') end def provider_url(provider) @@ -535,7 +543,8 @@ def provider_url(provider) wechat: "https://open.weixin.qq.com/connect/qrconnect?appid=#{::Sorcery::Controller::Config.wechat.key}&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=snsapi_login&state=#wechat_redirect", microsoft: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=#{::Sorcery::Controller::Config.microsoft.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=openid+email+https%3A%2F%2Fgraph.microsoft.com%2FUser.Read&state", instagram: "https://api.instagram.com/oauth/authorize?client_id=#{::Sorcery::Controller::Config.instagram.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=#{::Sorcery::Controller::Config.instagram.scope}&state", - auth0: "https://sorcery-test.auth0.com/authorize?client_id=#{::Sorcery::Controller::Config.auth0.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=openid+profile+email&state" + auth0: "https://sorcery-test.auth0.com/authorize?client_id=#{::Sorcery::Controller::Config.auth0.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=openid+profile+email&state", + discord: "https://discordapp.com/api/oauth2/authorize?client_id=#{::Sorcery::Controller::Config.discord.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=identify&state" }[provider] end end diff --git a/spec/rails_app/app/controllers/sorcery_controller.rb b/spec/rails_app/app/controllers/sorcery_controller.rb index a698ce3e..3f978b5a 100644 --- a/spec/rails_app/app/controllers/sorcery_controller.rb +++ b/spec/rails_app/app/controllers/sorcery_controller.rb @@ -158,6 +158,10 @@ def login_at_test_auth0 login_at(:auth0) end + def login_at_test_discord + login_at(:discord) + end + def test_login_from_twitter if (@user = login_from(:twitter)) redirect_to 'bla', notice: 'Success!' @@ -280,6 +284,14 @@ def test_login_from_line end end + def test_login_from_discord + if (@user = login_from(:discord)) + redirect_to 'bla', notice: 'Success!' + else + redirect_to 'blu', alert: 'Failed!' + end + end + def test_return_to_with_external_twitter if (@user = login_from(:twitter)) redirect_back_or_to 'bla', notice: 'Success!' @@ -402,6 +414,14 @@ def test_return_to_with_external_line end end + def test_return_to_with_external_discord + if (@user = login_from(:discord)) + redirect_back_or_to 'bla', notice: 'Success!' + else + redirect_to 'blu', alert: 'Failed!' + end + end + def test_create_from_provider provider = params[:provider] login_from(provider) diff --git a/spec/rails_app/config/routes.rb b/spec/rails_app/config/routes.rb index 66dbb240..2b9fc428 100644 --- a/spec/rails_app/config/routes.rb +++ b/spec/rails_app/config/routes.rb @@ -33,6 +33,7 @@ get :test_login_from_instagram get :test_login_from_auth0 get :test_login_from_line + get :test_login_from_discord get :login_at_test get :login_at_test_twitter get :login_at_test_facebook @@ -49,6 +50,7 @@ get :login_at_test_instagram get :login_at_test_auth0 get :login_at_test_line + get :login_at_test_discord get :test_return_to_with_external get :test_return_to_with_external_twitter get :test_return_to_with_external_facebook @@ -65,6 +67,7 @@ get :test_return_to_with_external_instagram get :test_return_to_with_external_auth0 get :test_return_to_with_external_line + get :test_return_to_with_external_discord get :test_http_basic_auth get :some_action_making_a_non_persisted_change_to_the_user post :test_login_with_remember From a973ae4d34c8442601dc89a1eca490d493b0e0ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mladen=20Ili=C4=87?= Date: Tue, 10 Dec 2019 00:58:07 +0100 Subject: [PATCH 066/112] Add ruby 2.6.5 to the travis build (#215) --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 6904be93..1ac53a5c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ rvm: - 2.3.6 - 2.4.3 - 2.5.0 + - 2.6.5 gemfile: - Gemfile From 26dd64bad3a56f35425b60e2431c40e9c5bb618a Mon Sep 17 00:00:00 2001 From: Toby Herbert <1735112+tobyaherbert@users.noreply.github.com> Date: Thu, 19 Dec 2019 00:32:20 +0000 Subject: [PATCH 067/112] Fix deprecation warnings in Rails 6 (#209) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Calling ActionController::Base causes deprecation warnings due to autoloading. ActionController::Base is autoloaded by Rails when called from the initializer in engine.rb which causes Rails 6 to raise deprecation warnings. * Added an empty manifest.js for specs The latest version of sprockets (4.0.0) now requires a manifest.js file in ‘app/assets/config/‘ or else an Exception is raised. * Deprecate Ruby Versions <= 2.3.x Per the following article, ruby versions 2.3 and below are deprecated: https://www.ruby-lang.org/en/news/2019/03/31/support-of-ruby-2-3-has-ended/ Ruby 2.4 will also be deprecated and should be removed by March 31, 2020. * Update Ruby 2.5.x to latest version (2.5.7) --- .rubocop.yml | 4 +- .rubocop_todo.yml | 140 +++++++++++++++++++++++++++++++++++++++++- .travis.yml | 6 +- lib/sorcery/engine.rb | 4 +- sorcery.gemspec | 6 +- 5 files changed, 148 insertions(+), 12 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index ce1dc552..fa6928d0 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -3,7 +3,7 @@ inherit_from: .rubocop_todo.yml AllCops: Exclude: - 'lib/generators/sorcery/templates/**/*' - TargetRubyVersion: 2.2 + TargetRubyVersion: 2.6 # See: https://github.com/rubocop-hq/rubocop/issues/3344 Style/DoubleNegation: @@ -21,7 +21,7 @@ Metrics/BlockLength: Exclude: - 'lib/**/*' - 'spec/**/*' -Metrics/LineLength: +Layout/LineLength: Exclude: - 'lib/**/*' - 'spec/**/*' diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 4d8c565f..67dfc312 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,7 +1,145 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2018-11-01 18:13:47 -0700 using RuboCop version 0.59.2. +# on 2019-12-18 16:18:24 -0800 using RuboCop version 0.78.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. + +# Offense count: 1 +# Configuration parameters: Include. +# Include: **/*.gemspec +Gemspec/RequiredRubyVersion: + Exclude: + - 'sorcery.gemspec' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: AllowAdjacentOneLineDefs, NumberOfEmptyLines. +Layout/EmptyLineBetweenDefs: + Exclude: + - 'lib/sorcery/providers/line.rb' + +# Offense count: 83 +# Cop supports --auto-correct. +# Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle. +# SupportedHashRocketStyles: key, separator, table +# SupportedColonStyles: key, separator, table +# SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit +Layout/HashAlignment: + Enabled: false + +# Offense count: 2 +# Configuration parameters: AllowSafeAssignment. +Lint/AssignmentInCondition: + Exclude: + - 'spec/rails_app/app/controllers/sorcery_controller.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Lint/NonDeterministicRequireOrder: + Exclude: + - 'spec/spec_helper.rb' + +# Offense count: 4 +# Cop supports --auto-correct. +Lint/RedundantCopDisableDirective: + Exclude: + - 'lib/sorcery/controller.rb' + - 'lib/sorcery/model.rb' + - 'spec/rails_app/config/application.rb' + - 'spec/shared_examples/user_shared_examples.rb' + +# Offense count: 4 +# Cop supports --auto-correct. +Lint/SendWithMixinArgument: + Exclude: + - 'lib/sorcery.rb' + - 'lib/sorcery/engine.rb' + - 'lib/sorcery/test_helpers/internal/rails.rb' + +# Offense count: 4 +# Configuration parameters: AllowComments. +Lint/SuppressedException: + Exclude: + - 'lib/sorcery/controller.rb' + - 'lib/sorcery/model.rb' + - 'spec/rails_app/config/application.rb' + - 'spec/shared_examples/user_shared_examples.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments. +Lint/UnusedBlockArgument: + Exclude: + - 'spec/shared_examples/user_shared_examples.rb' + +# Offense count: 1 +# Configuration parameters: EnforcedStyle. +# SupportedStyles: annotated, template, unannotated +Style/FormatStringToken: + Exclude: + - 'lib/generators/sorcery/install_generator.rb' + +# Offense count: 121 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: always, never +Style/FrozenStringLiteralComment: + Enabled: false + +# Offense count: 3 +# Configuration parameters: MinBodyLength. +Style/GuardClause: + Exclude: + - 'lib/sorcery/controller/submodules/brute_force_protection.rb' + - 'lib/sorcery/controller/submodules/http_basic_auth.rb' + - 'lib/sorcery/controller/submodules/remember_me.rb' + +# Offense count: 3 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols. +# SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys +Style/HashSyntax: + Exclude: + - 'lib/sorcery/adapters/active_record_adapter.rb' + - 'lib/sorcery/test_helpers/rails/integration.rb' + +# Offense count: 49 +# Cop supports --auto-correct. +Style/IfUnlessModifier: + Enabled: false + +# Offense count: 2 +# Cop supports --auto-correct. +Style/RedundantBegin: + Exclude: + - 'lib/sorcery/controller.rb' + - 'lib/sorcery/model.rb' + +# Offense count: 4 +# Cop supports --auto-correct. +# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods. +# AllowedMethods: present?, blank?, presence, try, try! +Style/SafeNavigation: + Exclude: + - 'lib/sorcery/controller/config.rb' + - 'lib/sorcery/controller/submodules/brute_force_protection.rb' + - 'lib/sorcery/controller/submodules/remember_me.rb' + - 'lib/sorcery/model.rb' + +# Offense count: 7 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. +# SupportedStyles: single_quotes, double_quotes +Style/StringLiterals: + Exclude: + - 'spec/controllers/controller_oauth2_spec.rb' + - 'spec/sorcery_crypto_providers_spec.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +Style/UnpackFirst: + Exclude: + - 'lib/sorcery/crypto_providers/aes256.rb' + - 'spec/sorcery_crypto_providers_spec.rb' diff --git a/.travis.yml b/.travis.yml index 1ac53a5c..95f9692f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,7 @@ language: ruby rvm: - - 2.2.9 - - 2.3.6 - - 2.4.3 - - 2.5.0 + - 2.4.9 + - 2.5.7 - 2.6.5 gemfile: diff --git a/lib/sorcery/engine.rb b/lib/sorcery/engine.rb index fd3bff16..ff0b7be5 100644 --- a/lib/sorcery/engine.rb +++ b/lib/sorcery/engine.rb @@ -9,11 +9,11 @@ class Engine < Rails::Engine initializer 'extend Controller with sorcery' do # TODO: Should this include a modified version of the helper methods? - if defined?(ActionController::API) + ActiveSupport.on_load(:action_controller_api) do ActionController::API.send(:include, Sorcery::Controller) end - if defined?(ActionController::Base) + ActiveSupport.on_load(:action_controller_base) do ActionController::Base.send(:include, Sorcery::Controller) ActionController::Base.helper_method :current_user ActionController::Base.helper_method :logged_in? diff --git a/sorcery.gemspec b/sorcery.gemspec index a62c40b0..3ec42c14 100644 --- a/sorcery.gemspec +++ b/sorcery.gemspec @@ -19,20 +19,20 @@ Gem::Specification.new do |s| ] # TODO: Cleanup formatting. - # rubocop:disable Metrics/LineLength + # rubocop:disable Layout/LineLength s.description = 'Provides common authentication needs such as signing in/out, activating by email and resetting password.' s.summary = 'Magical authentication for Rails applications' s.homepage = 'https://github.com/Sorcery/sorcery' s.post_install_message = "As of version 1.0 oauth/oauth2 won't be automatically bundled so you may need to add those dependencies to your Gemfile.\n" s.post_install_message += 'You may need oauth2 if you use external providers such as any of these: https://github.com/Sorcery/sorcery/tree/master/lib/sorcery/providers' - # rubocop:enable Metrics/LineLength + # rubocop:enable Layout/LineLength s.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR) s.require_paths = ['lib'] s.licenses = ['MIT'] - s.required_ruby_version = '>= 2.2.9' + s.required_ruby_version = '>= 2.4.9' s.add_dependency 'bcrypt', '~> 3.1' s.add_dependency 'oauth', '~> 0.4', '>= 0.4.4' From 16bb809e2c5198eba3592d333379d5d525802a3a Mon Sep 17 00:00:00 2001 From: MinJae Kwon Date: Tue, 31 Dec 2019 07:42:43 +0900 Subject: [PATCH 068/112] Fix typo (#219) * Fix typo * Fix 'an' to 'a' --- spec/controllers/controller_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/controllers/controller_spec.rb b/spec/controllers/controller_spec.rb index cd1e4e89..def1891b 100644 --- a/spec/controllers/controller_spec.rb +++ b/spec/controllers/controller_spec.rb @@ -171,7 +171,7 @@ # --- auto_login(user) --- specify { should respond_to(:auto_login) } - it 'auto_login(user) los in a user instance' do + it 'auto_login(user) logs in a user instance' do session[:user_id] = nil subject.auto_login(user) From f87d14e891100763386e0404c8ae17ac24755699 Mon Sep 17 00:00:00 2001 From: Christian Poplawski Date: Mon, 24 Feb 2020 18:06:33 +0100 Subject: [PATCH 069/112] In `generic_send_email`, check if mail object responds to delivery method instead of checking inheritance from `ActionMailer` (#211) * Check if mail object responds to delivery method Instead of checking for an inheritance of ActionMailer::Base, just see if the conffigure mailer and method respond to the configured delivery method. Since both the mailer and the willingniess to automatically send out messages can be configured, the message should be send if it is sendable, regardless of which Class the configured mailer inherits from. * Add empty mainifest.js to rails_app Not having a manifest was causing issues running `bundle exec rake` with sprockets 4. --- lib/sorcery/model.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sorcery/model.rb b/lib/sorcery/model.rb index f034cfa6..395710cf 100644 --- a/lib/sorcery/model.rb +++ b/lib/sorcery/model.rb @@ -206,7 +206,7 @@ def clear_virtual_password def generic_send_email(method, mailer) config = sorcery_config mail = config.send(mailer).send(config.send(method), self) - return unless defined?(ActionMailer) && config.send(mailer).is_a?(Class) && config.send(mailer) < ActionMailer::Base + return unless mail.respond_to?(config.email_delivery_method) mail.send(config.email_delivery_method) end From c30cefa751c364ee20b361eb63a766f782b69e5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mladen=20Ili=C4=87?= Date: Mon, 24 Feb 2020 18:17:59 +0100 Subject: [PATCH 070/112] Add forget_me! and force_forget_me! test cases (#216) --- .../controller_remember_me_spec.rb | 27 ++++++++++--------- .../app/controllers/sorcery_controller.rb | 8 ++++++ spec/rails_app/config/routes.rb | 1 + 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/spec/controllers/controller_remember_me_spec.rb b/spec/controllers/controller_remember_me_spec.rb index 5358bfcb..386060e3 100644 --- a/spec/controllers/controller_remember_me_spec.rb +++ b/spec/controllers/controller_remember_me_spec.rb @@ -6,14 +6,19 @@ # ----------------- REMEMBER ME ----------------------- context 'with remember me features' do before(:all) do + if SORCERY_ORM == :active_record + MigrationHelper.migrate("#{Rails.root}/db/migrate/remember_me") + User.reset_column_information + end + sorcery_reload!([:remember_me]) end - # TODO: Unused, remove? - # after(:each) do - # session = nil - # cookies = nil - # end + after(:all) do + if SORCERY_ORM == :active_record + MigrationHelper.rollback("#{Rails.root}/db/migrate/remember_me") + end + end before(:each) do allow(user).to receive(:remember_me_token) @@ -32,19 +37,17 @@ end it 'clears cookie on forget_me!' do - cookies['remember_me_token'] = { value: 'asd54234dsfsd43534', expires: 3600 } - get :test_logout + request.cookies[:remember_me_token] = { value: 'asd54234dsfsd43534', expires: 3600 } + get :test_logout_with_forget_me - pending 'Test previously broken, functionality might not be working here.' - expect(cookies['remember_me_token']).to be_nil + expect(response.cookies[:remember_me_token]).to be_nil end it 'clears cookie on force_forget_me!' do - cookies['remember_me_token'] = { value: 'asd54234dsfsd43534', expires: 3600 } + request.cookies[:remember_me_token] = { value: 'asd54234dsfsd43534', expires: 3600 } get :test_logout_with_force_forget_me - pending 'Test previously broken, functionality might not be working here.' - expect(cookies['remember_me_token']).to be_nil + expect(response.cookies[:remember_me_token]).to be_nil end it 'login(email,password,remember_me) logs user in and remembers' do diff --git a/spec/rails_app/app/controllers/sorcery_controller.rb b/spec/rails_app/app/controllers/sorcery_controller.rb index 3f978b5a..1f843c37 100644 --- a/spec/rails_app/app/controllers/sorcery_controller.rb +++ b/spec/rails_app/app/controllers/sorcery_controller.rb @@ -6,6 +6,7 @@ class SorceryController < ActionController::Base before_action :require_login_from_http_basic, only: [:test_http_basic_auth] before_action :require_login, only: %i[ test_logout + test_logout_with_forget_me test_logout_with_force_forget_me test_should_be_logged_in some_action @@ -50,6 +51,13 @@ def test_logout_with_remember head :ok end + def test_logout_with_forget_me + remember_me! + forget_me! + logout + head :ok + end + def test_logout_with_force_forget_me remember_me! force_forget_me! diff --git a/spec/rails_app/config/routes.rb b/spec/rails_app/config/routes.rb index 2b9fc428..1ae84fb4 100644 --- a/spec/rails_app/config/routes.rb +++ b/spec/rails_app/config/routes.rb @@ -11,6 +11,7 @@ get :test_login_from_cookie get :test_login_from get :test_logout_with_remember + get :test_logout_with_forget_me get :test_logout_with_force_forget_me get :test_invalidate_active_session get :test_should_be_logged_in From 6b72ca36cf389804963e2553ccbb3da000518e51 Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Fri, 1 May 2020 18:32:28 +0000 Subject: [PATCH 071/112] Revert on_load change due to breaking existing applications (#234) --- lib/sorcery/engine.rb | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/sorcery/engine.rb b/lib/sorcery/engine.rb index ff0b7be5..eb5de9b3 100644 --- a/lib/sorcery/engine.rb +++ b/lib/sorcery/engine.rb @@ -7,13 +7,19 @@ module Sorcery class Engine < Rails::Engine config.sorcery = ::Sorcery::Controller::Config + # TODO: Should this include a modified version of the helper methods? initializer 'extend Controller with sorcery' do - # TODO: Should this include a modified version of the helper methods? - ActiveSupport.on_load(:action_controller_api) do + # FIXME: on_load is needed to fix Rails 6 deprecations, but it breaks + # applications due to undefined method errors. + # ActiveSupport.on_load(:action_controller_api) do + if defined?(ActionController::API) ActionController::API.send(:include, Sorcery::Controller) end - ActiveSupport.on_load(:action_controller_base) do + # FIXME: on_load is needed to fix Rails 6 deprecations, but it breaks + # applications due to undefined method errors. + # ActiveSupport.on_load(:action_controller_base) do + if defined?(ActionController::Base) ActionController::Base.send(:include, Sorcery::Controller) ActionController::Base.helper_method :current_user ActionController::Base.helper_method :logged_in? From 0f116d223826895a73b12492f17486e5d54ab7a7 Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Sat, 2 May 2020 20:56:58 +0000 Subject: [PATCH 072/112] Fix brute force vuln due to callbacks not being ran (#235) The authenticate method previously would return before callbacks executed if an invalid password was provided, which causes the brute force protection to only work for the first lockout period, and only resets after a successful login. Fixes #231 --- lib/sorcery/model.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/sorcery/model.rb b/lib/sorcery/model.rb index 395710cf..6a500fc4 100644 --- a/lib/sorcery/model.rb +++ b/lib/sorcery/model.rb @@ -102,10 +102,6 @@ def authenticate(*credentials, &block) set_encryption_attributes - unless user.valid_password?(credentials[1]) - return authentication_response(user: user, failure: :invalid_password, &block) - end - if user.respond_to?(:active_for_authentication?) && !user.active_for_authentication? return authentication_response(user: user, failure: :inactive, &block) end @@ -118,6 +114,10 @@ def authenticate(*credentials, &block) end end + unless user.valid_password?(credentials[1]) + return authentication_response(user: user, failure: :invalid_password, &block) + end + authentication_response(user: user, return_value: user, &block) end From eee56533d90ec94a509c752f0e9178e7ee6fdd79 Mon Sep 17 00:00:00 2001 From: Joshua Buker Date: Sat, 2 May 2020 13:59:06 -0700 Subject: [PATCH 073/112] Add recent changes to changelog --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd564d86..f6eec9ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,21 @@ # Changelog ## HEAD +* Fix brute force vuln due to callbacks no being ran [#235](https://github.com/Sorcery/sorcery/pull/235) +* Revert on_load change due to breaking existing applications [#234](https://github.com/Sorcery/sorcery/pull/234) +* Add forget_me! and force_forget_me! test cases [#216](https://github.com/Sorcery/sorcery/pull/216) +* In `generic_send_email`, check responds_to [#211](https://github.com/Sorcery/sorcery/pull/211) +* Fix typo [#219](https://github.com/Sorcery/sorcery/pull/219) +* Fix deprecation warnings in Rails 6 [#209](https://github.com/Sorcery/sorcery/pull/209) +* Add ruby 2.6.5 to the travis build [#215](https://github.com/Sorcery/sorcery/pull/215) +* Add discord provider [#185](https://github.com/Sorcery/sorcery/pull/185) +* Remove MySQL database creation call [#214](https://github.com/Sorcery/sorcery/pull/214) +* Use id instead of uid for VK provider [#199](https://github.com/Sorcery/sorcery/pull/199) +* Don't :return_t JSON requests after login [#197](https://github.com/Sorcery/sorcery/pull/197) +* Fix email scope for LinkedIn Provider [#191](https://github.com/Sorcery/sorcery/pull/191) +* Ignore cookies when undefined cookies [#187](https://github.com/Sorcery/sorcery/pull/187) +* Allow for custom providers with multi-word class names. [#190](https://github.com/Sorcery/sorcery/pull/190) + ## 0.14.0 * Update LinkedIn to use OAuth 2 [#189](https://github.com/Sorcery/sorcery/pull/189) From e81c64c1775ad815ae42c75c4be0af41aed69e87 Mon Sep 17 00:00:00 2001 From: Joshua Buker Date: Sat, 2 May 2020 13:59:59 -0700 Subject: [PATCH 074/112] Release 0.15.0 --- CHANGELOG.md | 2 ++ lib/sorcery/version.rb | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6eec9ad..3ffffc4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## HEAD +## 0.15.0 + * Fix brute force vuln due to callbacks no being ran [#235](https://github.com/Sorcery/sorcery/pull/235) * Revert on_load change due to breaking existing applications [#234](https://github.com/Sorcery/sorcery/pull/234) * Add forget_me! and force_forget_me! test cases [#216](https://github.com/Sorcery/sorcery/pull/216) diff --git a/lib/sorcery/version.rb b/lib/sorcery/version.rb index 5d85c098..1c03a1d7 100644 --- a/lib/sorcery/version.rb +++ b/lib/sorcery/version.rb @@ -1,3 +1,3 @@ module Sorcery - VERSION = '0.14.0'.freeze + VERSION = '0.15.0'.freeze end From b6f70128db87f10c47febbb40dcbed48456ed65e Mon Sep 17 00:00:00 2001 From: Joshua Buker Date: Sat, 2 May 2020 15:16:27 -0700 Subject: [PATCH 075/112] Add pull request template --- .github/PULL_REQUEST_TEMPLATE.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..6bd1eef5 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,5 @@ +Please ensure your pull request includes the following: + +- [ ] Description of changes +- [ ] Update to CHANGELOG.md with short description and link to pull request +- [ ] Changes have related RSpec tests that ensure functionality does not break From 6ba63a4ff600b804b8072e06f5cf0782fb701f5a Mon Sep 17 00:00:00 2001 From: Steven Hoffman Date: Sat, 2 May 2020 15:18:41 -0700 Subject: [PATCH 076/112] Use Set to ensure unique arrays (#233) Signed-off-by: Steven Hoffman --- lib/sorcery/controller/config.rb | 12 ++++++------ .../controller/submodules/activity_logging.rb | 15 +++++---------- .../submodules/brute_force_protection.rb | 10 +++------- .../controller/submodules/http_basic_auth.rb | 6 ++---- lib/sorcery/controller/submodules/remember_me.rb | 10 +++------- .../controller/submodules/session_timeout.rb | 11 ++++------- 6 files changed, 23 insertions(+), 41 deletions(-) diff --git a/lib/sorcery/controller/config.rb b/lib/sorcery/controller/config.rb index 40e247f9..1a9bf112 100644 --- a/lib/sorcery/controller/config.rb +++ b/lib/sorcery/controller/config.rb @@ -25,12 +25,12 @@ def init! :@user_class => nil, :@submodules => [], :@not_authenticated_action => :not_authenticated, - :@login_sources => [], - :@after_login => [], - :@after_failed_login => [], - :@before_logout => [], - :@after_logout => [], - :@after_remember_me => [], + :@login_sources => Set.new, + :@after_login => Set.new, + :@after_failed_login => Set.new, + :@before_logout => Set.new, + :@after_logout => Set.new, + :@after_remember_me => Set.new, :@save_return_to_url => true, :@cookie_domain => nil } diff --git a/lib/sorcery/controller/submodules/activity_logging.rb b/lib/sorcery/controller/submodules/activity_logging.rb index 58782af6..2339b0a4 100644 --- a/lib/sorcery/controller/submodules/activity_logging.rb +++ b/lib/sorcery/controller/submodules/activity_logging.rb @@ -30,16 +30,11 @@ def merge_activity_logging_defaults! end merge_activity_logging_defaults! end - # FIXME: There is likely a more elegant way to safeguard these callbacks. - unless Config.after_login.include?(:register_login_time_to_db) - Config.after_login << :register_login_time_to_db - end - unless Config.after_login.include?(:register_last_ip_address) - Config.after_login << :register_last_ip_address - end - unless Config.before_logout.include?(:register_logout_time_to_db) - Config.before_logout << :register_logout_time_to_db - end + + Config.after_login << :register_login_time_to_db + Config.after_login << :register_last_ip_address + Config.before_logout << :register_logout_time_to_db + base.after_action :register_last_activity_time_to_db end diff --git a/lib/sorcery/controller/submodules/brute_force_protection.rb b/lib/sorcery/controller/submodules/brute_force_protection.rb index 67b12f87..e4756b7b 100644 --- a/lib/sorcery/controller/submodules/brute_force_protection.rb +++ b/lib/sorcery/controller/submodules/brute_force_protection.rb @@ -10,13 +10,9 @@ module Submodules module BruteForceProtection def self.included(base) base.send(:include, InstanceMethods) - # FIXME: There is likely a more elegant way to safeguard these callbacks. - unless Config.after_login.include?(:reset_failed_logins_count!) - Config.after_login << :reset_failed_logins_count! - end - unless Config.after_failed_login.include?(:update_failed_logins_count!) - Config.after_failed_login << :update_failed_logins_count! - end + + Config.after_login << :reset_failed_logins_count! + Config.after_failed_login << :update_failed_logins_count! end module InstanceMethods diff --git a/lib/sorcery/controller/submodules/http_basic_auth.rb b/lib/sorcery/controller/submodules/http_basic_auth.rb index e4cb265d..0f4ec34c 100644 --- a/lib/sorcery/controller/submodules/http_basic_auth.rb +++ b/lib/sorcery/controller/submodules/http_basic_auth.rb @@ -19,10 +19,8 @@ def merge_http_basic_auth_defaults! end merge_http_basic_auth_defaults! end - # FIXME: There is likely a more elegant way to safeguard these callbacks. - unless Config.login_sources.include?(:login_from_basic_auth) - Config.login_sources << :login_from_basic_auth - end + + Config.login_sources << :login_from_basic_auth end module InstanceMethods diff --git a/lib/sorcery/controller/submodules/remember_me.rb b/lib/sorcery/controller/submodules/remember_me.rb index 57af975d..e74daced 100644 --- a/lib/sorcery/controller/submodules/remember_me.rb +++ b/lib/sorcery/controller/submodules/remember_me.rb @@ -17,13 +17,9 @@ def merge_remember_me_defaults! end merge_remember_me_defaults! end - # FIXME: There is likely a more elegant way to safeguard these callbacks. - unless Config.login_sources.include?(:login_from_cookie) - Config.login_sources << :login_from_cookie - end - unless Config.before_logout.include?(:forget_me!) - Config.before_logout << :forget_me! - end + + Config.login_sources << :login_from_cookie + Config.before_logout << :forget_me! end module InstanceMethods diff --git a/lib/sorcery/controller/submodules/session_timeout.rb b/lib/sorcery/controller/submodules/session_timeout.rb index 0b0a02c2..f02b221f 100644 --- a/lib/sorcery/controller/submodules/session_timeout.rb +++ b/lib/sorcery/controller/submodules/session_timeout.rb @@ -23,13 +23,10 @@ def merge_session_timeout_defaults! end merge_session_timeout_defaults! end - # FIXME: There is likely a more elegant way to safeguard these callbacks. - unless Config.after_login.include?(:register_login_time) - Config.after_login << :register_login_time - end - unless Config.after_remember_me.include?(:register_login_time) - Config.after_remember_me << :register_login_time - end + + Config.after_login << :register_login_time + Config.after_remember_me << :register_login_time + base.prepend_before_action :validate_session end From 65065ef2ae416cae0de08c9f0dd016343009d59d Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Thu, 11 Jun 2020 18:09:36 +0000 Subject: [PATCH 077/112] Create SECURITY.md --- SECURITY.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..9a80192b --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,18 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +| -------- | ------------------ | +| 0.15.0 | :white_check_mark: | +| < 0.15.0 | :x: | + +## Reporting a Vulnerability + +Email the current maintainer(s) with a description of the vulnerability. You +should expect a response within 48 hours. If the vulnerability is accepted, a +Github advisory will be created and eventually released with a CVE corresponding +to the issue found. + +A list of the current maintainers can be found on the README under the contact +section. See: [README.md](https://github.com/Sorcery/sorcery#contact) From f2c4fcf95e3cf4bc2055e8b1eeca36abe2e40650 Mon Sep 17 00:00:00 2001 From: Takumi Shotoku Date: Wed, 17 Jun 2020 05:30:47 +0900 Subject: [PATCH 078/112] Fix keyword arguments warnings on Ruby 2.7 (#241) * CI against Ruby 2.7 * Fix keyword arguments warnings on Ruby 2.7 It fixes codes to match method signatures in rails and bcrypt. ## See also - https://github.com/rails/rails/blob/v6.0.3/activerecord/lib/active_record/persistence.rb#L469 - https://github.com/rails/rails/blob/v6.0.3/activemodel/lib/active_model/callbacks.rb - https://github.com/codahale/bcrypt-ruby/blob/v3.1.13/lib/bcrypt/password.rb#L43 * Update CHANGELOG.md Co-authored-by: Josh Buker --- .travis.yml | 1 + CHANGELOG.md | 2 ++ lib/sorcery/adapters/active_record_adapter.rb | 4 ++-- spec/shared_examples/user_shared_examples.rb | 4 ++-- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 95f9692f..ecd6dfdb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ rvm: - 2.4.9 - 2.5.7 - 2.6.5 + - 2.7.1 gemfile: - Gemfile diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ffffc4b..19814976 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## HEAD +* Fix ruby 2.7 deprecation warnings [#241](https://github.com/Sorcery/sorcery/pull/241) + ## 0.15.0 * Fix brute force vuln due to callbacks no being ran [#235](https://github.com/Sorcery/sorcery/pull/235) diff --git a/lib/sorcery/adapters/active_record_adapter.rb b/lib/sorcery/adapters/active_record_adapter.rb index c778fa48..dac9facf 100644 --- a/lib/sorcery/adapters/active_record_adapter.rb +++ b/lib/sorcery/adapters/active_record_adapter.rb @@ -12,7 +12,7 @@ def update_attributes(attrs) def save(options = {}) mthd = options.delete(:raise_on_failure) ? :save! : :save - @model.send(mthd, options) + @model.send(mthd, **options) end def increment(field) @@ -35,7 +35,7 @@ def define_field(name, type, options = {}) end def define_callback(time, event, method_name, options = {}) - @klass.send "#{time}_#{event}", method_name, options.slice(:if, :on) + @klass.send "#{time}_#{event}", method_name, **options.slice(:if, :on) end def find_by_oauth_credentials(provider, uid) diff --git a/spec/shared_examples/user_shared_examples.rb b/spec/shared_examples/user_shared_examples.rb index 39324b89..c1e09620 100644 --- a/spec/shared_examples/user_shared_examples.rb +++ b/spec/shared_examples/user_shared_examples.rb @@ -511,7 +511,7 @@ def self.matches?(crypted, *tokens) # password comparison is done using BCrypt::Password#==(raw_token), not String#== bcrypt_password = BCrypt::Password.new(user.crypted_password) - allow(::BCrypt::Password).to receive(:create) do |token, cost:| + allow(::BCrypt::Password).to receive(:create) do |token, options = {}| # need to use common BCrypt's salt when genarating BCrypt::Password objects # so that any generated password hashes can be compared each other ::BCrypt::Engine.hash_secret(token, bcrypt_password.salt) @@ -535,7 +535,7 @@ def self.matches?(crypted, *tokens) # password comparison is done using BCrypt::Password#==(raw_token), not String#== bcrypt_password = BCrypt::Password.new(user.crypted_password) - allow(::BCrypt::Password).to receive(:create) do |token, cost:| + allow(::BCrypt::Password).to receive(:create) do |token, options = {}| # need to use common BCrypt's salt when genarating BCrypt::Password objects # so that any generated password hashes can be compared each other ::BCrypt::Engine.hash_secret(token, bcrypt_password.salt) From 31a055b2b6f26261958c348fcf9602f87a558ab5 Mon Sep 17 00:00:00 2001 From: Takumi Shotoku Date: Wed, 17 Jun 2020 05:35:48 +0900 Subject: [PATCH 079/112] Add support for Rails 6 (#238) * Cache bundler on Travis * Add support for Rails 6 Since the usage of `MigrationContext` was changed in https://github.com/rails/rails/pull/36439, adds a branch to MigrationHelper. We are loading 'rails/all' in our test code, it implicitly load action_text from Rails 6. ActionText requires ApplicationController, adds it. ref: https://github.com/rails/rails/blob/v6.0.3/actiontext/lib/action_text/engine.rb#L50 * Exclude Ruby 2.4 for Rails 6 Rails 6 does't support Ruby 2.4. ref: https://github.com/rails/rails/pull/34754 --- .travis.yml | 10 +++++++++- Gemfile | 4 ++-- gemfiles/rails_52.gemfile | 7 +++++++ gemfiles/rails_60.gemfile | 7 +++++++ .../app/controllers/application_controller.rb | 2 ++ .../app/controllers/sorcery_controller.rb | 2 +- spec/support/migration_helper.rb | 14 ++++++++++++-- 7 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 gemfiles/rails_52.gemfile create mode 100644 gemfiles/rails_60.gemfile create mode 100644 spec/rails_app/app/controllers/application_controller.rb diff --git a/.travis.yml b/.travis.yml index ecd6dfdb..4ed54c11 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,6 @@ language: ruby +cache: bundler + rvm: - 2.4.9 - 2.5.7 @@ -6,4 +8,10 @@ rvm: - 2.7.1 gemfile: - - Gemfile + - gemfiles/rails_52.gemfile + - gemfiles/rails_60.gemfile + +jobs: + exclude: + - rvm: 2.4.9 + gemfile: gemfiles/rails_60.gemfile diff --git a/Gemfile b/Gemfile index 9ee5d84f..532fc3ce 100644 --- a/Gemfile +++ b/Gemfile @@ -1,8 +1,8 @@ source 'https://rubygems.org' gem 'pry' -gem 'rails', '~> 5.2.0' +gem 'rails' gem 'rails-controller-testing' -gem 'sqlite3', '~> 1.3.6' +gem 'sqlite3' gemspec diff --git a/gemfiles/rails_52.gemfile b/gemfiles/rails_52.gemfile new file mode 100644 index 00000000..ff1c7211 --- /dev/null +++ b/gemfiles/rails_52.gemfile @@ -0,0 +1,7 @@ +source 'https://rubygems.org' + +gem 'rails', '~> 5.2.0' +gem 'rails-controller-testing' +gem 'sqlite3', '~> 1.3.6' + +gemspec path: '..' diff --git a/gemfiles/rails_60.gemfile b/gemfiles/rails_60.gemfile new file mode 100644 index 00000000..cae26b8b --- /dev/null +++ b/gemfiles/rails_60.gemfile @@ -0,0 +1,7 @@ +source 'https://rubygems.org' + +gem 'rails', '~> 6.0.0' +gem 'rails-controller-testing' +gem 'sqlite3', '~> 1.4' + +gemspec path: '..' diff --git a/spec/rails_app/app/controllers/application_controller.rb b/spec/rails_app/app/controllers/application_controller.rb new file mode 100644 index 00000000..09705d12 --- /dev/null +++ b/spec/rails_app/app/controllers/application_controller.rb @@ -0,0 +1,2 @@ +class ApplicationController < ActionController::Base +end diff --git a/spec/rails_app/app/controllers/sorcery_controller.rb b/spec/rails_app/app/controllers/sorcery_controller.rb index 1f843c37..94e8e5de 100644 --- a/spec/rails_app/app/controllers/sorcery_controller.rb +++ b/spec/rails_app/app/controllers/sorcery_controller.rb @@ -1,6 +1,6 @@ require 'oauth' -class SorceryController < ActionController::Base +class SorceryController < ApplicationController protect_from_forgery before_action :require_login_from_http_basic, only: [:test_http_basic_auth] diff --git a/spec/support/migration_helper.rb b/spec/support/migration_helper.rb index 27c8f299..5f6e9501 100644 --- a/spec/support/migration_helper.rb +++ b/spec/support/migration_helper.rb @@ -1,7 +1,9 @@ class MigrationHelper class << self def migrate(path) - if ActiveRecord.version >= Gem::Version.new('5.2.0') + if ActiveRecord.version >= Gem::Version.new('6.0.0') + ActiveRecord::MigrationContext.new(path, schema_migration).migrate + elsif ActiveRecord.version >= Gem::Version.new('5.2.0') ActiveRecord::MigrationContext.new(path).migrate else ActiveRecord::Migrator.migrate(path) @@ -9,11 +11,19 @@ def migrate(path) end def rollback(path) - if ActiveRecord.version >= Gem::Version.new('5.2.0') + if ActiveRecord.version >= Gem::Version.new('6.0.0') + ActiveRecord::MigrationContext.new(path, schema_migration).rollback + elsif ActiveRecord.version >= Gem::Version.new('5.2.0') ActiveRecord::MigrationContext.new(path).rollback else ActiveRecord::Migrator.rollback(path) end end + + private + + def schema_migration + ActiveRecord::Base.connection.schema_migration + end end end From 178079135c97a2fd28d52b02f7aaaa027681e2ff Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Sat, 27 Jun 2020 02:03:05 -0700 Subject: [PATCH 080/112] Encourage users to look at the issues page --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index c99830ae..1e9f2df2 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,12 @@ [![Inline docs](http://inch-ci.org/github/Sorcery/sorcery.svg?branch=master)](http://inch-ci.org/github/Sorcery/sorcery) [![Join the chat at https://gitter.im/Sorcery/sorcery](https://badges.gitter.im/join_chat.svg)](https://gitter.im/Sorcery/sorcery?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +--- + +**IMPORTANT:** Sorcery needs your help and feedback! Please look at issues [#204](https://github.com/Sorcery/sorcery/issues/204) and [#248](https://github.com/Sorcery/sorcery/issues/248). + +--- + Magical Authentication for Rails. Supports ActiveRecord, DataMapper, Mongoid and MongoMapper. Inspired by Restful Authentication, Authlogic and Devise. Crypto code taken almost unchanged from Authlogic. OAuth code inspired by OmniAuth and Ryan Bates's Railscast about it. From c5c2cce848c934e09acaed7a3d325f01f932b8b1 Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Sat, 27 Jun 2020 02:15:07 -0700 Subject: [PATCH 081/112] Update Chase to past maintainer --- README.md | 2 +- sorcery.gemspec | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1e9f2df2..891ac773 100644 --- a/README.md +++ b/README.md @@ -244,7 +244,6 @@ Feel free to ask questions using these contact details: **Current Maintainers:** -- Chase Gilliam ([@Ch4s3](https://github.com/Ch4s3)) | [Email](mailto:chase.gilliam@gmail.com) - Josh Buker ([@athix](https://github.com/athix)) | [Email](mailto:jbuker@aeonsplice.com) **Past Maintainers:** @@ -252,6 +251,7 @@ Feel free to ask questions using these contact details: - Noam Ben-Ari ([@NoamB](https://github.com/NoamB)) | [Email](mailto:nbenari@gmail.com) | [Twitter](https://twitter.com/nbenari) - Kir Shatrov ([@kirs](https://github.com/kirs)) | [Email](mailto:shatrov@me.com) | [Twitter](https://twitter.com/Kiiiir) - Grzegorz Witek ([@arnvald](https://github.com/arnvald)) | [Email](mailto:arnvald.to@gmail.com) | [Twitter](https://twitter.com/arnvald) +- Chase Gilliam ([@Ch4s3](https://github.com/Ch4s3)) | [Email](mailto:chase.gilliam@gmail.com) ## License diff --git a/sorcery.gemspec b/sorcery.gemspec index 3ec42c14..7a90e92b 100644 --- a/sorcery.gemspec +++ b/sorcery.gemspec @@ -14,8 +14,7 @@ Gem::Specification.new do |s| 'Josh Buker' ] s.email = [ - 'chase.gilliam@gmail.com', - 'contact@joshbuker.com' + 'crypto@joshbuker.com' ] # TODO: Cleanup formatting. From 28d0884764739510ca6fd48bb0d634974b52f312 Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Sat, 27 Jun 2020 02:19:03 -0700 Subject: [PATCH 082/112] Update contact details --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 891ac773..632a0b6b 100644 --- a/README.md +++ b/README.md @@ -244,7 +244,7 @@ Feel free to ask questions using these contact details: **Current Maintainers:** -- Josh Buker ([@athix](https://github.com/athix)) | [Email](mailto:jbuker@aeonsplice.com) +- Josh Buker ([@athix](https://github.com/athix)) | [Email](mailto:crypto+sorcery@joshbuker.com?subject=Sorcery) **Past Maintainers:** From 54ec819819e193b6fde3419e5469c57f2966d894 Mon Sep 17 00:00:00 2001 From: Seth T <13125514+crimson-knight@users.noreply.github.com> Date: Sun, 5 Jul 2020 13:53:20 -0400 Subject: [PATCH 083/112] Updated the generators to allow namespaced models to be passed in, such as "ExampleNameSpace::User" parametere which will generate a table migration with "example_name_space_user" as the table name (#237) --- lib/generators/sorcery/helpers.rb | 4 ++++ .../sorcery/templates/migration/activity_logging.rb | 10 +++++----- .../templates/migration/brute_force_protection.rb | 8 ++++---- lib/generators/sorcery/templates/migration/core.rb | 4 ++-- lib/generators/sorcery/templates/migration/external.rb | 2 +- .../sorcery/templates/migration/magic_login.rb | 8 ++++---- .../sorcery/templates/migration/remember_me.rb | 6 +++--- .../sorcery/templates/migration/reset_password.rb | 10 +++++----- .../sorcery/templates/migration/user_activation.rb | 8 ++++---- 9 files changed, 32 insertions(+), 28 deletions(-) diff --git a/lib/generators/sorcery/helpers.rb b/lib/generators/sorcery/helpers.rb index f0830785..5bc94408 100644 --- a/lib/generators/sorcery/helpers.rb +++ b/lib/generators/sorcery/helpers.rb @@ -12,6 +12,10 @@ def model_class_name options[:model] ? options[:model].classify : 'User' end + def tableized_model_class + options[:model] ? options[:model].gsub(/::/, '').tableize : 'User' + end + def model_path @model_path ||= File.join('app', 'models', "#{file_path}.rb") end diff --git a/lib/generators/sorcery/templates/migration/activity_logging.rb b/lib/generators/sorcery/templates/migration/activity_logging.rb index c1b3edd6..ed905ce7 100644 --- a/lib/generators/sorcery/templates/migration/activity_logging.rb +++ b/lib/generators/sorcery/templates/migration/activity_logging.rb @@ -1,10 +1,10 @@ class SorceryActivityLogging < <%= migration_class_name %> def change - add_column :<%= model_class_name.tableize %>, :last_login_at, :datetime, default: nil - add_column :<%= model_class_name.tableize %>, :last_logout_at, :datetime, default: nil - add_column :<%= model_class_name.tableize %>, :last_activity_at, :datetime, default: nil - add_column :<%= model_class_name.tableize %>, :last_login_from_ip_address, :string, default: nil + add_column :<%= tableized_model_class %>, :last_login_at, :datetime, default: nil + add_column :<%= tableized_model_class %>, :last_logout_at, :datetime, default: nil + add_column :<%= tableized_model_class %>, :last_activity_at, :datetime, default: nil + add_column :<%= tableized_model_class %>, :last_login_from_ip_address, :string, default: nil - add_index :<%= model_class_name.tableize %>, [:last_logout_at, :last_activity_at] + add_index :<%= tableized_model_class %>, [:last_logout_at, :last_activity_at] end end diff --git a/lib/generators/sorcery/templates/migration/brute_force_protection.rb b/lib/generators/sorcery/templates/migration/brute_force_protection.rb index 64ea19a8..ea549180 100644 --- a/lib/generators/sorcery/templates/migration/brute_force_protection.rb +++ b/lib/generators/sorcery/templates/migration/brute_force_protection.rb @@ -1,9 +1,9 @@ class SorceryBruteForceProtection < <%= migration_class_name %> def change - add_column :<%= model_class_name.tableize %>, :failed_logins_count, :integer, default: 0 - add_column :<%= model_class_name.tableize %>, :lock_expires_at, :datetime, default: nil - add_column :<%= model_class_name.tableize %>, :unlock_token, :string, default: nil + add_column :<%= tableized_model_class %>, :failed_logins_count, :integer, default: 0 + add_column :<%= tableized_model_class %>, :lock_expires_at, :datetime, default: nil + add_column :<%= tableized_model_class %>, :unlock_token, :string, default: nil - add_index :<%= model_class_name.tableize %>, :unlock_token + add_index :<%= tableized_model_class %>, :unlock_token end end diff --git a/lib/generators/sorcery/templates/migration/core.rb b/lib/generators/sorcery/templates/migration/core.rb index 1ca5287b..a7174eb4 100644 --- a/lib/generators/sorcery/templates/migration/core.rb +++ b/lib/generators/sorcery/templates/migration/core.rb @@ -1,6 +1,6 @@ class SorceryCore < <%= migration_class_name %> def change - create_table :<%= model_class_name.tableize %> do |t| + create_table :<%= tableized_model_class %> do |t| t.string :email, null: false t.string :crypted_password t.string :salt @@ -8,6 +8,6 @@ def change t.timestamps null: false end - add_index :<%= model_class_name.tableize %>, :email, unique: true + add_index :<%= tableized_model_class %>, :email, unique: true end end diff --git a/lib/generators/sorcery/templates/migration/external.rb b/lib/generators/sorcery/templates/migration/external.rb index 08541f3b..e8b7a960 100644 --- a/lib/generators/sorcery/templates/migration/external.rb +++ b/lib/generators/sorcery/templates/migration/external.rb @@ -1,7 +1,7 @@ class SorceryExternal < <%= migration_class_name %> def change create_table :authentications do |t| - t.integer :<%= model_class_name.tableize.singularize %>_id, null: false + t.integer :<%= tableized_model_class.singularize %>_id, null: false t.string :provider, :uid, null: false t.timestamps null: false diff --git a/lib/generators/sorcery/templates/migration/magic_login.rb b/lib/generators/sorcery/templates/migration/magic_login.rb index b9a10a6c..73d4aff2 100644 --- a/lib/generators/sorcery/templates/migration/magic_login.rb +++ b/lib/generators/sorcery/templates/migration/magic_login.rb @@ -1,9 +1,9 @@ class SorceryMagicLogin < <%= migration_class_name %> def change - add_column :<%= model_class_name.tableize %>, :magic_login_token, :string, default: nil - add_column :<%= model_class_name.tableize %>, :magic_login_token_expires_at, :datetime, default: nil - add_column :<%= model_class_name.tableize %>, :magic_login_email_sent_at, :datetime, default: nil + add_column :<%= tableized_model_class %>, :magic_login_token, :string, default: nil + add_column :<%= tableized_model_class %>, :magic_login_token_expires_at, :datetime, default: nil + add_column :<%= tableized_model_class %>, :magic_login_email_sent_at, :datetime, default: nil - add_index :<%= model_class_name.tableize %>, :magic_login_token + add_index :<%= tableized_model_class %>, :magic_login_token end end diff --git a/lib/generators/sorcery/templates/migration/remember_me.rb b/lib/generators/sorcery/templates/migration/remember_me.rb index a27f7b69..37370536 100644 --- a/lib/generators/sorcery/templates/migration/remember_me.rb +++ b/lib/generators/sorcery/templates/migration/remember_me.rb @@ -1,8 +1,8 @@ class SorceryRememberMe < <%= migration_class_name %> def change - add_column :<%= model_class_name.tableize %>, :remember_me_token, :string, default: nil - add_column :<%= model_class_name.tableize %>, :remember_me_token_expires_at, :datetime, default: nil + add_column :<%= tableized_model_class %>, :remember_me_token, :string, default: nil + add_column :<%= tableized_model_class %>, :remember_me_token_expires_at, :datetime, default: nil - add_index :<%= model_class_name.tableize %>, :remember_me_token + add_index :<%= tableized_model_class %>, :remember_me_token end end diff --git a/lib/generators/sorcery/templates/migration/reset_password.rb b/lib/generators/sorcery/templates/migration/reset_password.rb index 9cf66c8c..708f6c11 100644 --- a/lib/generators/sorcery/templates/migration/reset_password.rb +++ b/lib/generators/sorcery/templates/migration/reset_password.rb @@ -1,10 +1,10 @@ class SorceryResetPassword < <%= migration_class_name %> def change - add_column :<%= model_class_name.tableize %>, :reset_password_token, :string, default: nil - add_column :<%= model_class_name.tableize %>, :reset_password_token_expires_at, :datetime, default: nil - add_column :<%= model_class_name.tableize %>, :reset_password_email_sent_at, :datetime, default: nil - add_column :<%= model_class_name.tableize %>, :access_count_to_reset_password_page, :integer, default: 0 + add_column :<%= tableized_model_class %>, :reset_password_token, :string, default: nil + add_column :<%= tableized_model_class %>, :reset_password_token_expires_at, :datetime, default: nil + add_column :<%= tableized_model_class %>, :reset_password_email_sent_at, :datetime, default: nil + add_column :<%= tableized_model_class %>, :access_count_to_reset_password_page, :integer, default: 0 - add_index :<%= model_class_name.tableize %>, :reset_password_token + add_index :<%= tableized_model_class %>, :reset_password_token end end diff --git a/lib/generators/sorcery/templates/migration/user_activation.rb b/lib/generators/sorcery/templates/migration/user_activation.rb index cc5dc19d..f115286f 100644 --- a/lib/generators/sorcery/templates/migration/user_activation.rb +++ b/lib/generators/sorcery/templates/migration/user_activation.rb @@ -1,9 +1,9 @@ class SorceryUserActivation < <%= migration_class_name %> def change - add_column :<%= model_class_name.tableize %>, :activation_state, :string, default: nil - add_column :<%= model_class_name.tableize %>, :activation_token, :string, default: nil - add_column :<%= model_class_name.tableize %>, :activation_token_expires_at, :datetime, default: nil + add_column :<%= tableized_model_class %>, :activation_state, :string, default: nil + add_column :<%= tableized_model_class %>, :activation_token, :string, default: nil + add_column :<%= tableized_model_class %>, :activation_token_expires_at, :datetime, default: nil - add_index :<%= model_class_name.tableize %>, :activation_token + add_index :<%= tableized_model_class %>, :activation_token end end From a6cef7e42d619dc45b46c9326705c8d4e1bd2b13 Mon Sep 17 00:00:00 2001 From: horiken Date: Tue, 7 Jul 2020 16:22:46 +0900 Subject: [PATCH 084/112] Support line login v2.1 (#251) --- .../sorcery/templates/initializer.rb | 3 +++ lib/sorcery/providers/line.rb | 24 +++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/lib/generators/sorcery/templates/initializer.rb b/lib/generators/sorcery/templates/initializer.rb index 9b7829de..18f1aa14 100644 --- a/lib/generators/sorcery/templates/initializer.rb +++ b/lib/generators/sorcery/templates/initializer.rb @@ -222,6 +222,9 @@ # config.line.key = "" # config.line.secret = "" # config.line.callback_url = "http://mydomain.com:3000/oauth/callback?provider=line" + # config.line.scope = "profile" + # config.line.bot_prompt = "normal" + # config.line.user_info_mapping = {name: 'displayName'} # For infromation about Discord API # https://discordapp.com/developers/docs/topics/oauth2 diff --git a/lib/sorcery/providers/line.rb b/lib/sorcery/providers/line.rb index b903490e..7ad87c98 100644 --- a/lib/sorcery/providers/line.rb +++ b/lib/sorcery/providers/line.rb @@ -9,15 +9,16 @@ module Providers class Line < Base include Protocols::Oauth2 - attr_accessor :token_url, :user_info_path, :auth_path + attr_accessor :token_url, :user_info_path, :auth_path, :scope, :bot_prompt def initialize super @site = 'https://access.line.me' @user_info_path = 'https://api.line.me/v2/profile' - @token_url = 'https://api.line.me/v2/oauth/accessToken' - @auth_path = 'dialog/oauth/weblogin' + @token_url = 'https://api.line.me/oauth2/v2.1/token' + @auth_path = 'oauth2/v2.1/authorize' + @scope = 'profile' end def get_user_hash(access_token) @@ -34,13 +35,28 @@ def login_url(_params, _session) @state = SecureRandom.hex(16) authorize_url(authorize_url: auth_path) end + + # overrides oauth2#authorize_url to add bot_prompt query. + def authorize_url(options = {}) + options.merge!({ + connection_opts: { params: { bot_prompt: bot_prompt } } + }) if bot_prompt.present? + + super(options) + end + # tries to login the user from access token def process_callback(params, _session) args = {}.tap do |a| a[:code] = params[:code] if params[:code] end - get_access_token(args, token_url: token_url, token_method: :post) + get_access_token( + args, + token_url: token_url, + token_method: :post, + grant_type: 'authorization_code' + ) end end end From 5d4c2d4dca912da49a98af59eadb347a7135ca8b Mon Sep 17 00:00:00 2001 From: Takumi Shotoku Date: Tue, 7 Jul 2020 16:23:39 +0900 Subject: [PATCH 085/112] Fix failing isolated tests (#249) --- spec/controllers/controller_oauth2_spec.rb | 9 +++++++++ spec/controllers/controller_oauth_spec.rb | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/spec/controllers/controller_oauth2_spec.rb b/spec/controllers/controller_oauth2_spec.rb index 2c849eaf..fa8efca3 100644 --- a/spec/controllers/controller_oauth2_spec.rb +++ b/spec/controllers/controller_oauth2_spec.rb @@ -116,12 +116,21 @@ end context 'when callback_url begin with http://' do + before do + sorcery_controller_external_property_set(:facebook, :callback_url, '/oauth/twitter/callback') + sorcery_controller_external_property_set(:facebook, :api_version, 'v2.2') + end + it 'login_at redirects correctly' do create_new_user get :login_at_test_facebook expect(response).to be_a_redirect expect(response).to redirect_to("https://www.facebook.com/v2.2/dialog/oauth?client_id=#{::Sorcery::Controller::Config.facebook.key}&display=page&redirect_uri=http%3A%2F%2Ftest.host%2Foauth%2Ftwitter%2Fcallback&response_type=code&scope=email&state") end + + after do + sorcery_controller_external_property_set(:facebook, :callback_url, 'http://blabla.com') + end end it "'login_from' logins if user exists" do diff --git a/spec/controllers/controller_oauth_spec.rb b/spec/controllers/controller_oauth_spec.rb index 31086a0c..1cac58bf 100644 --- a/spec/controllers/controller_oauth_spec.rb +++ b/spec/controllers/controller_oauth_spec.rb @@ -84,11 +84,17 @@ def stub_all_oauth_requests! end context 'when callback_url begin with http://' do + before do + sorcery_controller_external_property_set(:twitter, :callback_url, '/oauth/twitter/callback') + end it 'login_at redirects correctly', pending: true do get :login_at_test expect(response).to be_a_redirect expect(response).to redirect_to('http://myapi.com/oauth/authorize?oauth_callback=http%3A%2F%2Fblabla.com&oauth_token=') end + after do + sorcery_controller_external_property_set(:twitter, :callback_url, 'http://blabla.com') + end end it 'logins if user exists' do From 569c35f6c1906b243731e963ab635e822af08167 Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Wed, 15 Jul 2020 19:09:22 +0000 Subject: [PATCH 086/112] Update README.md Remove issue notice, solid plan is in place now. --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 632a0b6b..8ae70b80 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,6 @@ [![Inline docs](http://inch-ci.org/github/Sorcery/sorcery.svg?branch=master)](http://inch-ci.org/github/Sorcery/sorcery) [![Join the chat at https://gitter.im/Sorcery/sorcery](https://badges.gitter.im/join_chat.svg)](https://gitter.im/Sorcery/sorcery?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ---- - -**IMPORTANT:** Sorcery needs your help and feedback! Please look at issues [#204](https://github.com/Sorcery/sorcery/issues/204) and [#248](https://github.com/Sorcery/sorcery/issues/248). - ---- - Magical Authentication for Rails. Supports ActiveRecord, DataMapper, Mongoid and MongoMapper. Inspired by Restful Authentication, Authlogic and Devise. Crypto code taken almost unchanged from Authlogic. OAuth code inspired by OmniAuth and Ryan Bates's Railscast about it. From a02c1247642357129ea739354c22978a06fccaa9 Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Wed, 22 Jul 2020 22:29:54 -0700 Subject: [PATCH 087/112] Add CODE_OF_CONDUCT.md This is essentially 1:1 with Rubocop's Code of Conduct, which itself uses Ruby's Code of Conduct as a base. It's minimal and easy to understand, which mirrors the ethos of the Sorcery library itself. --- CODE_OF_CONDUCT.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..fa802928 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,14 @@ +# The Sorcery Community Code of Conduct + +This document provides a few simple community guidelines for a safe, respectful, +productive, and collaborative place for any person who is willing to contribute +to the Sorcery community. It applies to all "collaborative spaces", which are +defined as community communications channels (such as mailing lists, submitted +patches, commit comments, etc.). + +* Participants will be tolerant of opposing views. +* Participants must ensure that their language and actions are free of personal + attacks and disparaging personal remarks. +* When interpreting the words and actions of others, participants should always + assume good intentions. +* Behaviour which can be reasonably considered harassment will not be tolerated. From d16fffa6e0eceb1b2e7b0337d53faab8227c01f0 Mon Sep 17 00:00:00 2001 From: Johannes Handke Date: Sun, 29 Nov 2020 19:45:53 +0100 Subject: [PATCH 088/112] Add Battlenet Provider (#260) * Add BattleNet Provider * Add Changelog Info for BattleNet Provider * Add BattleNet Tests * Fix Errors * Fix Typo in Battlenet Provider * Add Site config to initializer * Fix Typo * Remove conditional set of code arg --- CHANGELOG.md | 1 + .../sorcery/templates/initializer.rb | 11 +++- lib/sorcery/controller/submodules/external.rb | 4 ++ lib/sorcery/providers/battlenet.rb | 51 +++++++++++++++++++ spec/controllers/controller_oauth2_spec.rb | 19 +++++-- .../app/controllers/sorcery_controller.rb | 20 ++++++++ spec/rails_app/config/routes.rb | 3 ++ 7 files changed, 103 insertions(+), 6 deletions(-) create mode 100644 lib/sorcery/providers/battlenet.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 19814976..5d7e194a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## HEAD +* Add BattleNet Provider [#260](https://github.com/Sorcery/sorcery/pull/260) * Fix ruby 2.7 deprecation warnings [#241](https://github.com/Sorcery/sorcery/pull/241) ## 0.15.0 diff --git a/lib/generators/sorcery/templates/initializer.rb b/lib/generators/sorcery/templates/initializer.rb index 18f1aa14..66b39ca3 100644 --- a/lib/generators/sorcery/templates/initializer.rb +++ b/lib/generators/sorcery/templates/initializer.rb @@ -226,12 +226,21 @@ # config.line.bot_prompt = "normal" # config.line.user_info_mapping = {name: 'displayName'} - # For infromation about Discord API + + # For information about Discord API # https://discordapp.com/developers/docs/topics/oauth2 # config.discord.key = "xxxxxx" # config.discord.secret = "xxxxxx" # config.discord.callback_url = "http://localhost:3000/oauth/callback?provider=discord" # config.discord.scope = "email guilds" + + # For information about Battlenet API + # https://develop.battle.net/documentation/guides/using-oauth + # config.battlenet.site = "https://eu.battle.net/" #See Website for other Regional Domains + # config.battlenet.key = "xxxxxx" + # config.battlenet.secret = "xxxxxx" + # config.battlenet.callback_url = "http://localhost:3000/oauth/callback?provider=battlenet" + # config.battlenet.scope = "openid" # --- user config --- config.user_config do |user| # -- core -- diff --git a/lib/sorcery/controller/submodules/external.rb b/lib/sorcery/controller/submodules/external.rb index 07e755d2..6ec7f8c9 100644 --- a/lib/sorcery/controller/submodules/external.rb +++ b/lib/sorcery/controller/submodules/external.rb @@ -27,6 +27,10 @@ def self.included(base) require 'sorcery/providers/auth0' require 'sorcery/providers/line' require 'sorcery/providers/discord' + require 'sorcery/providers/battlenet' + + + Config.module_eval do class << self diff --git a/lib/sorcery/providers/battlenet.rb b/lib/sorcery/providers/battlenet.rb new file mode 100644 index 00000000..3e486eef --- /dev/null +++ b/lib/sorcery/providers/battlenet.rb @@ -0,0 +1,51 @@ +module Sorcery + module Providers + # This class adds support for OAuth with BattleNet + + class Battlenet < Base + include Protocols::Oauth2 + + attr_accessor :auth_path, :scope, :token_url, :user_info_path + + def initialize + super + + @scope = 'openid' + @site = 'https://eu.battle.net/' + @auth_path = '/oauth/authorize' + @token_url = '/oauth/token' + @user_info_path = '/oauth/userinfo' + @state = SecureRandom.hex(16) + end + + def get_user_hash(access_token) + response = access_token.get(user_info_path) + body = JSON.parse(response.body) + auth_hash(access_token).tap do |h| + h[:user_info] = body + h[:battletag] = body['battletag'] + h[:uid] = body['id'] + end + end + + # calculates and returns the url to which the user should be redirected, + # to get authenticated at the external provider's site. + def login_url(_params, _session) + authorize_url(authorize_url: auth_path) + end + + # tries to login the user from access token + def process_callback(params, _session) + args = { code: params[:code] } + get_access_token( + args, + token_url: token_url, + client_id: @key, + client_secret: @secret, + grant_type: 'authorization_code', + token_method: :post + ) + end + end + end +end diff --git a/spec/controllers/controller_oauth2_spec.rb b/spec/controllers/controller_oauth2_spec.rb index fa8efca3..0732c3b4 100644 --- a/spec/controllers/controller_oauth2_spec.rb +++ b/spec/controllers/controller_oauth2_spec.rb @@ -164,7 +164,7 @@ expect(flash[:notice]).to eq 'Success!' end - %i[github google liveid vk salesforce paypal slack wechat microsoft instagram auth0 discord].each do |provider| + %i[github google liveid vk salesforce paypal slack wechat microsoft instagram auth0 discord battlenet].each do |provider| describe "with #{provider}" do it 'login_at redirects correctly' do get :"login_at_test_#{provider}" @@ -227,6 +227,7 @@ auth0 line discord + battlenet ] ) @@ -274,6 +275,9 @@ sorcery_controller_external_property_set(:discord, :key, 'eYVNBjBDi33aa9GkA3w') sorcery_controller_external_property_set(:discord, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') sorcery_controller_external_property_set(:discord, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:battlenet, :key, '4c43d4862c774ca5bbde89873bf0d338') + sorcery_controller_external_property_set(:battlenet, :secret, 'TxY7IwKOykACd8kUxPyVGTqBs44UBDdX') + sorcery_controller_external_property_set(:battlenet, :callback_url, 'http://blabla.com') end after(:each) do @@ -296,7 +300,7 @@ expect(ActionMailer::Base.deliveries.size).to eq old_size end - %i[github google liveid vk salesforce paypal wechat microsoft instagram auth0 discord].each do |provider| + %i[github google liveid vk salesforce paypal wechat microsoft instagram auth0 discord battlenet].each do |provider| it "does not send activation email to external users (#{provider})" do old_size = ActionMailer::Base.deliveries.size create_new_external_user provider @@ -320,7 +324,7 @@ sorcery_reload!(%i[activity_logging external]) end - %w[facebook github google liveid vk salesforce slack discord].each do |provider| + %w[facebook github google liveid vk salesforce slack discord battlenet].each do |provider| context "when #{provider}" do before(:each) do sorcery_controller_property_set(:register_login_time, true) @@ -359,7 +363,7 @@ let(:user) { double('user', id: 42) } - %w[facebook github google liveid vk salesforce slack discord].each do |provider| + %w[facebook github google liveid vk salesforce slack discord battlenet].each do |provider| context "when #{provider}" do before(:each) do sorcery_model_property_set(:authentications_class, Authentication) @@ -493,6 +497,7 @@ def set_external_property auth0 line discord + battlenet ] ) sorcery_controller_external_property_set(:facebook, :key, 'eYVNBjBDi33aa9GkA3w') @@ -538,6 +543,9 @@ def set_external_property sorcery_controller_external_property_set(:discord, :key, 'eYVNBjBDi33aa9GkA3w') sorcery_controller_external_property_set(:discord, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8') sorcery_controller_external_property_set(:discord, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:battlenet, :key, '4c43d4862c774ca5bbde89873bf0d338') + sorcery_controller_external_property_set(:battlenet, :secret, 'TxY7IwKOykACd8kUxPyVGTqBs44UBDdX') + sorcery_controller_external_property_set(:battlenet, :callback_url, 'http://blabla.com') end def provider_url(provider) @@ -553,7 +561,8 @@ def provider_url(provider) microsoft: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=#{::Sorcery::Controller::Config.microsoft.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=openid+email+https%3A%2F%2Fgraph.microsoft.com%2FUser.Read&state", instagram: "https://api.instagram.com/oauth/authorize?client_id=#{::Sorcery::Controller::Config.instagram.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=#{::Sorcery::Controller::Config.instagram.scope}&state", auth0: "https://sorcery-test.auth0.com/authorize?client_id=#{::Sorcery::Controller::Config.auth0.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=openid+profile+email&state", - discord: "https://discordapp.com/api/oauth2/authorize?client_id=#{::Sorcery::Controller::Config.discord.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=identify&state" + discord: "https://discordapp.com/api/oauth2/authorize?client_id=#{::Sorcery::Controller::Config.discord.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=identify&state", + battlenet: "https://eu.battle.net/oauth/authorize?client_id=#{::Sorcery::Controller::Config.battlenet.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=openid&state" }[provider] end end diff --git a/spec/rails_app/app/controllers/sorcery_controller.rb b/spec/rails_app/app/controllers/sorcery_controller.rb index 94e8e5de..8214e36a 100644 --- a/spec/rails_app/app/controllers/sorcery_controller.rb +++ b/spec/rails_app/app/controllers/sorcery_controller.rb @@ -170,6 +170,10 @@ def login_at_test_discord login_at(:discord) end + def login_at_test_battlenet + login_at(:battlenet) + end + def test_login_from_twitter if (@user = login_from(:twitter)) redirect_to 'bla', notice: 'Success!' @@ -300,6 +304,14 @@ def test_login_from_discord end end + def test_login_from_battlenet + if (@user = login_from(:battlenet)) + redirect_to 'bla', notice: 'Success!' + else + redirect_to 'blu', alert: 'Failed!' + end + end + def test_return_to_with_external_twitter if (@user = login_from(:twitter)) redirect_back_or_to 'bla', notice: 'Success!' @@ -430,6 +442,14 @@ def test_return_to_with_external_discord end end + def test_return_to_with_external_battlenet + if (@user = login_from(:battlenet)) + redirect_back_or_to 'bla', notice: 'Success!' + else + redirect_to 'blu', alert: 'Failed!' + end + end + def test_create_from_provider provider = params[:provider] login_from(provider) diff --git a/spec/rails_app/config/routes.rb b/spec/rails_app/config/routes.rb index 1ae84fb4..4bf82c29 100644 --- a/spec/rails_app/config/routes.rb +++ b/spec/rails_app/config/routes.rb @@ -35,6 +35,7 @@ get :test_login_from_auth0 get :test_login_from_line get :test_login_from_discord + get :test_login_from_battlenet get :login_at_test get :login_at_test_twitter get :login_at_test_facebook @@ -52,6 +53,7 @@ get :login_at_test_auth0 get :login_at_test_line get :login_at_test_discord + get :login_at_test_battlenet get :test_return_to_with_external get :test_return_to_with_external_twitter get :test_return_to_with_external_facebook @@ -69,6 +71,7 @@ get :test_return_to_with_external_auth0 get :test_return_to_with_external_line get :test_return_to_with_external_discord + get :test_return_to_with_external_battlenet get :test_http_basic_auth get :some_action_making_a_non_persisted_change_to_the_user post :test_login_with_remember From a939ca90fb2f33b31b7735e86443e2a10acda8e7 Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Sat, 2 Jan 2021 07:49:06 +0000 Subject: [PATCH 089/112] Create ruby.yml --- .github/workflows/ruby.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/ruby.yml diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml new file mode 100644 index 00000000..8bbcfede --- /dev/null +++ b/.github/workflows/ruby.yml @@ -0,0 +1,25 @@ +name: Ruby + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + test: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 2.6 + - name: Install dependencies + run: bundle install + - name: Run tests + run: bundle exec rake + - name: Run rubocop + run: bundle exec rubocop From 93eff2d6a16ebdc3e2ca7478633260ebd0956363 Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Fri, 1 Jan 2021 23:53:47 -0800 Subject: [PATCH 090/112] Update rubocop todo --- .rubocop_todo.yml | 62 +++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 67dfc312..62a5a808 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2019-12-18 16:18:24 -0800 using RuboCop version 0.78.0. +# on 2021-01-02 07:53:19 UTC using RuboCop version 0.88.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -13,12 +13,12 @@ Gemspec/RequiredRubyVersion: Exclude: - 'sorcery.gemspec' -# Offense count: 1 +# Offense count: 2 # Cop supports --auto-correct. -# Configuration parameters: AllowAdjacentOneLineDefs, NumberOfEmptyLines. -Layout/EmptyLineBetweenDefs: - Exclude: - - 'lib/sorcery/providers/line.rb' +# Configuration parameters: IndentationWidth. +# SupportedStyles: special_inside_parentheses, consistent, align_braces +Layout/FirstHashElementIndentation: + EnforcedStyle: consistent # Offense count: 83 # Cop supports --auto-correct. @@ -29,6 +29,13 @@ Layout/EmptyLineBetweenDefs: Layout/HashAlignment: Enabled: false +# Offense count: 3 +# Cop supports --auto-correct. +# Configuration parameters: AllowInHeredoc. +Layout/TrailingWhitespace: + Exclude: + - 'lib/sorcery/controller/submodules/external.rb' + # Offense count: 2 # Configuration parameters: AllowSafeAssignment. Lint/AssignmentInCondition: @@ -58,15 +65,6 @@ Lint/SendWithMixinArgument: - 'lib/sorcery/engine.rb' - 'lib/sorcery/test_helpers/internal/rails.rb' -# Offense count: 4 -# Configuration parameters: AllowComments. -Lint/SuppressedException: - Exclude: - - 'lib/sorcery/controller.rb' - - 'lib/sorcery/model.rb' - - 'spec/rails_app/config/application.rb' - - 'spec/shared_examples/user_shared_examples.rb' - # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments. @@ -74,6 +72,20 @@ Lint/UnusedBlockArgument: Exclude: - 'spec/shared_examples/user_shared_examples.rb' +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SingleLineConditionsOnly, IncludeTernaryExpressions. +# SupportedStyles: assign_to_condition, assign_inside_condition +Style/ConditionalAssignment: + Exclude: + - 'lib/sorcery/adapters/active_record_adapter.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Style/ExpandPathArguments: + Exclude: + - 'spec/rails_app/config.ru' + # Offense count: 1 # Configuration parameters: EnforcedStyle. # SupportedStyles: annotated, template, unannotated @@ -81,21 +93,13 @@ Style/FormatStringToken: Exclude: - 'lib/generators/sorcery/install_generator.rb' -# Offense count: 121 +# Offense count: 125 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. -# SupportedStyles: always, never +# SupportedStyles: always, always_true, never Style/FrozenStringLiteralComment: Enabled: false -# Offense count: 3 -# Configuration parameters: MinBodyLength. -Style/GuardClause: - Exclude: - - 'lib/sorcery/controller/submodules/brute_force_protection.rb' - - 'lib/sorcery/controller/submodules/http_basic_auth.rb' - - 'lib/sorcery/controller/submodules/remember_me.rb' - # Offense count: 3 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols. @@ -105,11 +109,17 @@ Style/HashSyntax: - 'lib/sorcery/adapters/active_record_adapter.rb' - 'lib/sorcery/test_helpers/rails/integration.rb' -# Offense count: 49 +# Offense count: 34 # Cop supports --auto-correct. Style/IfUnlessModifier: Enabled: false +# Offense count: 1 +# Cop supports --auto-correct. +Style/MultilineIfModifier: + Exclude: + - 'lib/sorcery/providers/line.rb' + # Offense count: 2 # Cop supports --auto-correct. Style/RedundantBegin: From 443e614e3d2dfc9d7dd805c4a69f8e1c820c7e76 Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Fri, 1 Jan 2021 23:59:27 -0800 Subject: [PATCH 091/112] Remove rubocop from github workflow --- .github/workflows/ruby.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 8bbcfede..613821b1 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -21,5 +21,3 @@ jobs: run: bundle install - name: Run tests run: bundle exec rake - - name: Run rubocop - run: bundle exec rubocop From 43efa3329e61d3fff8a5233d9efdc90db6820362 Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Tue, 16 Feb 2021 11:31:23 -0800 Subject: [PATCH 092/112] Release 0.16.0 --- CHANGELOG.md | 7 +++++++ lib/sorcery/version.rb | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d7e194a..c1cf5301 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,15 @@ # Changelog ## HEAD +## 0.16.0 + * Add BattleNet Provider [#260](https://github.com/Sorcery/sorcery/pull/260) +* Fix failing isolated tests [#249](https://github.com/Sorcery/sorcery/pull/249) +* Support LINE login v2.1 [#251](https://github.com/Sorcery/sorcery/pull/251) +* Update generators to better support namespaces [#237](https://github.com/Sorcery/sorcery/pull/237) +* Add support for Rails 6 [#238](https://github.com/Sorcery/sorcery/pull/238) * Fix ruby 2.7 deprecation warnings [#241](https://github.com/Sorcery/sorcery/pull/241) +* Use set to ensure unique arrays [#233](https://github.com/Sorcery/sorcery/pull/233) ## 0.15.0 diff --git a/lib/sorcery/version.rb b/lib/sorcery/version.rb index 1c03a1d7..966efed2 100644 --- a/lib/sorcery/version.rb +++ b/lib/sorcery/version.rb @@ -1,3 +1,3 @@ module Sorcery - VERSION = '0.15.0'.freeze + VERSION = '0.16.0'.freeze end From 404862dcde43e292c2b450a7283a4d88291fd557 Mon Sep 17 00:00:00 2001 From: Teruhisa Fukumoto Date: Tue, 16 Mar 2021 02:03:39 +0900 Subject: [PATCH 093/112] Remove InchCI badge - Service no longer available (#272) --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 8ae70b80..1f3b374f 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,6 @@ [![Gem Downloads](https://img.shields.io/gem/dt/sorcery.svg)](https://rubygems.org/gems/sorcery) [![Build Status](https://travis-ci.org/Sorcery/sorcery.svg?branch=master)](https://travis-ci.org/Sorcery/sorcery) [![Code Climate](https://codeclimate.com/github/Sorcery/sorcery.svg)](https://codeclimate.com/github/Sorcery/sorcery) -[![Inline docs](http://inch-ci.org/github/Sorcery/sorcery.svg?branch=master)](http://inch-ci.org/github/Sorcery/sorcery) [![Join the chat at https://gitter.im/Sorcery/sorcery](https://badges.gitter.im/join_chat.svg)](https://gitter.im/Sorcery/sorcery?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) Magical Authentication for Rails. Supports ActiveRecord, DataMapper, Mongoid and MongoMapper. From bf6eb580d5b71501f9edc2097245b3106e135d77 Mon Sep 17 00:00:00 2001 From: Leszek Zalewski Date: Wed, 17 Mar 2021 17:22:55 +0100 Subject: [PATCH 094/112] Update github action to test against version matrix (#267) * Test against ruby 3 * Switch from Travis to GH Workflows * Add metastep for simplified require GH checks * Fix typo in matrix * Remove TravisCI config * Exclude rails 5.2 from ruby 3, deprecation conflicts Co-authored-by: Josh Buker --- .github/workflows/ruby.yml | 49 ++++++++++++++++++++++++++++++-------- .travis.yml | 17 ------------- 2 files changed, 39 insertions(+), 27 deletions(-) delete mode 100644 .travis.yml diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 613821b1..3c510018 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -7,17 +7,46 @@ on: branches: [ master ] jobs: - test: + test_matrix: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + ruby: + - 2.4.10 + - 2.5.8 + - 2.6.6 + - 2.7.2 + - 3.0.0 + + rails: + - '52' + - '60' + + exclude: + - ruby: 2.4.10 + rails: '60' + - ruby: 3.0.0 + rails: '52' + + env: + BUNDLE_GEMFILE: gemfiles/rails_${{ matrix.rails }}.gemfile + + steps: + - uses: actions/checkout@v2 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - name: Run tests + run: bundle exec rake + + test: + runs-on: ubuntu-latest + needs: [test_matrix] steps: - - uses: actions/checkout@v2 - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: 2.6 - - name: Install dependencies - run: bundle install - - name: Run tests - run: bundle exec rake + - name: Wait for status checks + run: echo "All Green!" diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 4ed54c11..00000000 --- a/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -language: ruby -cache: bundler - -rvm: - - 2.4.9 - - 2.5.7 - - 2.6.5 - - 2.7.1 - -gemfile: - - gemfiles/rails_52.gemfile - - gemfiles/rails_60.gemfile - -jobs: - exclude: - - rvm: 2.4.9 - gemfile: gemfiles/rails_60.gemfile From 58ff4ac8dbbbd5bb52e825c538621ec422793971 Mon Sep 17 00:00:00 2001 From: linyclar <73414727+linyclar@users.noreply.github.com> Date: Fri, 19 Mar 2021 01:16:26 +0900 Subject: [PATCH 095/112] Fix default table name being incorrect in migration generator (#274) --- lib/generators/sorcery/helpers.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/generators/sorcery/helpers.rb b/lib/generators/sorcery/helpers.rb index 5bc94408..3f37d0cb 100644 --- a/lib/generators/sorcery/helpers.rb +++ b/lib/generators/sorcery/helpers.rb @@ -13,7 +13,7 @@ def model_class_name end def tableized_model_class - options[:model] ? options[:model].gsub(/::/, '').tableize : 'User' + options[:model] ? options[:model].gsub(/::/, '').tableize : 'users' end def model_path From 6fdc703416b3ff8d05708b05d5a8228ab39032a5 Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Fri, 2 Apr 2021 16:28:04 -0700 Subject: [PATCH 096/112] Update supported versions to include 0.16.X --- SECURITY.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index 9a80192b..e1d53e77 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,10 +2,11 @@ ## Supported Versions -| Version | Supported | -| -------- | ------------------ | -| 0.15.0 | :white_check_mark: | -| < 0.15.0 | :x: | +| Version | Supported | +| --------- | ------------------ | +| ~> 0.16.0 | :white_check_mark: | +| ~> 0.15.0 | :white_check_mark: | +| < 0.15.0 | :x: | ## Reporting a Vulnerability From a26ed98d002f0ac8dfab2c463990c80710be91cd Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Sat, 3 Apr 2021 21:50:51 -0700 Subject: [PATCH 097/112] Update github action to match rework formatting --- .github/workflows/ruby.yml | 47 +++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 3c510018..d7b61df9 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -1,10 +1,7 @@ -name: Ruby +name: Test Suite -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] +# Run against all commits and pull requests. +on: [ push, pull_request ] jobs: test_matrix: @@ -15,20 +12,20 @@ jobs: fail-fast: false matrix: ruby: - - 2.4.10 - - 2.5.8 - - 2.6.6 - - 2.7.2 - - 3.0.0 + - 2.4 + - 2.5 + - 2.6 + - 2.7 + - 3.0 rails: - '52' - '60' exclude: - - ruby: 2.4.10 + - ruby: 2.4 rails: '60' - - ruby: 3.0.0 + - ruby: 3.0 rails: '52' env: @@ -42,11 +39,29 @@ jobs: ruby-version: ${{ matrix.ruby }} bundler-cache: true - name: Run tests - run: bundle exec rake + run: bundle exec rake spec - test: + rubocop: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.0 + bundler-cache: true + - name: Run rubocop + run: bundle exec rake rubocop + +# TODO: Add code coverage testing (coveralls) +# TODO: Add documentation/maintainability testing? +# TODO: Add dependency testing? (bundle audit) + + finish: runs-on: ubuntu-latest - needs: [test_matrix] + needs: [ test_matrix, rubocop ] steps: - name: Wait for status checks run: echo "All Green!" From 5d9583443ccd791f0697687cbbcb2918fd2f1417 Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Sat, 3 Apr 2021 22:00:41 -0700 Subject: [PATCH 098/112] Fix rake not including the rubocop task Honestly, I don't know if I want to even bother with rubocop in the legacy codebase. The styling is being closely monitored in the rework, and code style isn't really critical when it comes to security patches and bug fixes. All to say, I may just disable the rubocop job for the legacy code again at some point, if it gets annoying. --- .rubocop_todo.yml | 10 +++++++++- Rakefile | 4 +++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 62a5a808..9cb44610 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2021-01-02 07:53:19 UTC using RuboCop version 0.88.0. +# on 2021-04-04 05:00:11 UTC using RuboCop version 0.88.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -147,6 +147,14 @@ Style/StringLiterals: - 'spec/controllers/controller_oauth2_spec.rb' - 'spec/sorcery_crypto_providers_spec.rb' +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, MinSize. +# SupportedStyles: percent, brackets +Style/SymbolArray: + Exclude: + - 'Rakefile' + # Offense count: 2 # Cop supports --auto-correct. Style/UnpackFirst: diff --git a/Rakefile b/Rakefile index 2470119d..d44e016c 100644 --- a/Rakefile +++ b/Rakefile @@ -1,6 +1,8 @@ require 'bundler/gem_tasks' require 'rspec/core/rake_task' +require 'rubocop/rake_task' RSpec::Core::RakeTask.new(:spec) +RuboCop::RakeTask.new -task default: :spec +task default: [:rubocop, :spec] From 0783e42f099547c3a4a52db48e19082dba4a295b Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Wed, 7 Apr 2021 09:47:27 -0700 Subject: [PATCH 099/112] Remove random whitespace --- lib/sorcery/controller/submodules/external.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/sorcery/controller/submodules/external.rb b/lib/sorcery/controller/submodules/external.rb index 6ec7f8c9..07114572 100644 --- a/lib/sorcery/controller/submodules/external.rb +++ b/lib/sorcery/controller/submodules/external.rb @@ -28,9 +28,6 @@ def self.included(base) require 'sorcery/providers/line' require 'sorcery/providers/discord' require 'sorcery/providers/battlenet' - - - Config.module_eval do class << self From 5fae86fcaf836e32d29ff456ce96bcb65dda6922 Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Wed, 7 Apr 2021 09:48:44 -0700 Subject: [PATCH 100/112] Goodbye rubocop, see you in v1 --- .github/workflows/ruby.yml | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index d7b61df9..a965e5d1 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -41,27 +41,9 @@ jobs: - name: Run tests run: bundle exec rake spec - rubocop: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: 3.0 - bundler-cache: true - - name: Run rubocop - run: bundle exec rake rubocop - -# TODO: Add code coverage testing (coveralls) -# TODO: Add documentation/maintainability testing? -# TODO: Add dependency testing? (bundle audit) - finish: runs-on: ubuntu-latest - needs: [ test_matrix, rubocop ] + needs: [ test_matrix ] steps: - name: Wait for status checks run: echo "All Green!" From 40fc1d04f2523c9a59a1ce76994280e557640d1c Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Sat, 24 Apr 2021 11:35:27 -0700 Subject: [PATCH 101/112] Add sponsor button --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..ea071307 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: athix From 5f17be1019a20e32ebf4447f88cf7e20d342903b Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Fri, 30 Apr 2021 12:46:56 -0700 Subject: [PATCH 102/112] Update oauth dependency per CVE-2016-11086 --- CHANGELOG.md | 4 ++++ sorcery.gemspec | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ffffc4b..b5bf8e29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog ## HEAD +## 0.15.1 + +* Update `oauth` dependency per CVE-2016-11086 + ## 0.15.0 * Fix brute force vuln due to callbacks no being ran [#235](https://github.com/Sorcery/sorcery/pull/235) diff --git a/sorcery.gemspec b/sorcery.gemspec index 3ec42c14..07d383f0 100644 --- a/sorcery.gemspec +++ b/sorcery.gemspec @@ -35,7 +35,7 @@ Gem::Specification.new do |s| s.required_ruby_version = '>= 2.4.9' s.add_dependency 'bcrypt', '~> 3.1' - s.add_dependency 'oauth', '~> 0.4', '>= 0.4.4' + s.add_dependency 'oauth', '~> 0.5', '>= 0.5.5' s.add_dependency 'oauth2', '~> 1.0', '>= 0.8.0' s.add_development_dependency 'byebug', '~> 10.0.0' From 9cc74ea68a81de277c304ab98d89fd71c82eb13e Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Fri, 30 Apr 2021 12:47:14 -0700 Subject: [PATCH 103/112] Release v0.15.1 --- lib/sorcery/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sorcery/version.rb b/lib/sorcery/version.rb index 1c03a1d7..ce5f2dfa 100644 --- a/lib/sorcery/version.rb +++ b/lib/sorcery/version.rb @@ -1,3 +1,3 @@ module Sorcery - VERSION = '0.15.0'.freeze + VERSION = '0.15.1'.freeze end From c460403f24255fa5f3d4b42d31d4a632a8c1a560 Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Fri, 30 Apr 2021 12:54:43 -0700 Subject: [PATCH 104/112] Release v0.16.1 --- CHANGELOG.md | 5 +++++ lib/sorcery/version.rb | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53f4792d..d91c18b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Changelog ## HEAD +## 0.16.1 + +* Fix default table name being incorrect in migration generator [#274](https://github.com/Sorcery/sorcery/pull/274) +* Update `oauth` dependency per CVE-2016-11086 + ## 0.16.0 * Add BattleNet Provider [#260](https://github.com/Sorcery/sorcery/pull/260) diff --git a/lib/sorcery/version.rb b/lib/sorcery/version.rb index 966efed2..6a15147e 100644 --- a/lib/sorcery/version.rb +++ b/lib/sorcery/version.rb @@ -1,3 +1,3 @@ module Sorcery - VERSION = '0.16.0'.freeze + VERSION = '0.16.1'.freeze end From 107a5473a7d4b86beb55f90809b3d2980c1249ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mladen=20Ili=C4=87?= Date: Sat, 5 Jun 2021 02:39:35 +0200 Subject: [PATCH 105/112] Inline index definition in core migration (#281) --- lib/generators/sorcery/templates/migration/core.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/generators/sorcery/templates/migration/core.rb b/lib/generators/sorcery/templates/migration/core.rb index a7174eb4..b1ce82d8 100644 --- a/lib/generators/sorcery/templates/migration/core.rb +++ b/lib/generators/sorcery/templates/migration/core.rb @@ -1,13 +1,11 @@ class SorceryCore < <%= migration_class_name %> def change create_table :<%= tableized_model_class %> do |t| - t.string :email, null: false + t.string :email, null: false, index: { unique: true } t.string :crypted_password t.string :salt t.timestamps null: false end - - add_index :<%= tableized_model_class %>, :email, unique: true end end From 829683b4bffc321d5c8940e00dcf545c2d2c04b6 Mon Sep 17 00:00:00 2001 From: Robin van der Vleuten Date: Sat, 5 Jun 2021 02:40:59 +0200 Subject: [PATCH 106/112] Add missing remember_me attributes to config (#180) --- lib/generators/sorcery/templates/initializer.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/generators/sorcery/templates/initializer.rb b/lib/generators/sorcery/templates/initializer.rb index 66b39ca3..c4fe62f4 100644 --- a/lib/generators/sorcery/templates/initializer.rb +++ b/lib/generators/sorcery/templates/initializer.rb @@ -306,6 +306,16 @@ # user.subclasses_inherit_config = # -- remember_me -- + # change default remember_me_token attribute. + # Default: `:remember_me_token` + # + # user.remember_me_token_attribute_name = + + # change default remember_me_token_expires_at attribute. + # Default: `:remember_me_token_expires_at` + # + # user.remember_me_token_expires_at_attribute_name = + # How long in seconds the session length will be # Default: `60 * 60 * 24 * 7` # From 8f1064f29905f2ac149a90ae5acdcf35679c3b66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mladen=20Ili=C4=87?= Date: Wed, 9 Jun 2021 04:18:38 +0200 Subject: [PATCH 107/112] Update changelog (#283) --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d91c18b9..ba0d9c90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## HEAD +* Inline core migration index definition [#281](https://github.com/Sorcery/sorcery/pull/281) + ## 0.16.1 * Fix default table name being incorrect in migration generator [#274](https://github.com/Sorcery/sorcery/pull/274) From 4f7ea0ba8673e3f49d6347554989bd06c8d337cb Mon Sep 17 00:00:00 2001 From: Josh Buker Date: Sun, 20 Jun 2021 01:17:25 -0700 Subject: [PATCH 108/112] Fix MongoID adapter breaking on save (#284) * Update mongoid adapter to use splat operator for options * Add changelog entry --- CHANGELOG.md | 1 + lib/sorcery/adapters/mongoid_adapter.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba0d9c90..6c6aacb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## HEAD * Inline core migration index definition [#281](https://github.com/Sorcery/sorcery/pull/281) +* Fix MongoID adapter breaking on save [#284](https://github.com/Sorcery/sorcery/pull/284) ## 0.16.1 diff --git a/lib/sorcery/adapters/mongoid_adapter.rb b/lib/sorcery/adapters/mongoid_adapter.rb index fbf5e23d..438a5095 100644 --- a/lib/sorcery/adapters/mongoid_adapter.rb +++ b/lib/sorcery/adapters/mongoid_adapter.rb @@ -32,7 +32,7 @@ def define_field(name, type, options = {}) end def define_callback(time, event, method_name, options = {}) - @klass.send callback_name(time, event, options), method_name, options.slice(:if) + @klass.send callback_name(time, event, options), method_name, **options.slice(:if) end def callback_name(time, event, options) From 2e2d0fe4f20960f4ab691f13457b98bccaa4642a Mon Sep 17 00:00:00 2001 From: bealking Date: Mon, 25 Sep 2017 14:43:19 +0800 Subject: [PATCH 109/112] add qq provider --- .../sorcery/templates/initializer.rb | 6 +- lib/sorcery/controller/submodules/external.rb | 1 + lib/sorcery/providers/qq.rb | 76 +++++++++++++++++++ spec/controllers/controller_oauth2_spec.rb | 32 +++++++- .../app/controllers/sorcery_controller.rb | 20 +++++ spec/rails_app/config/routes.rb | 3 + 6 files changed, 133 insertions(+), 5 deletions(-) create mode 100644 lib/sorcery/providers/qq.rb diff --git a/lib/generators/sorcery/templates/initializer.rb b/lib/generators/sorcery/templates/initializer.rb index c4fe62f4..6dbbaea3 100644 --- a/lib/generators/sorcery/templates/initializer.rb +++ b/lib/generators/sorcery/templates/initializer.rb @@ -158,6 +158,10 @@ # config.auth0.callback_url = "https://0.0.0.0:3000/oauth/callback?provider=auth0" # config.auth0.site = "https://example.auth0.com" # + # config.qq.key = "" + # config.qq.secret = "" + # config.qq.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=qq" + # # config.google.key = "" # config.google.secret = "" # config.google.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=google" @@ -226,7 +230,7 @@ # config.line.bot_prompt = "normal" # config.line.user_info_mapping = {name: 'displayName'} - + # For information about Discord API # https://discordapp.com/developers/docs/topics/oauth2 # config.discord.key = "xxxxxx" diff --git a/lib/sorcery/controller/submodules/external.rb b/lib/sorcery/controller/submodules/external.rb index 07114572..9b67b6e3 100644 --- a/lib/sorcery/controller/submodules/external.rb +++ b/lib/sorcery/controller/submodules/external.rb @@ -22,6 +22,7 @@ def self.included(base) require 'sorcery/providers/paypal' require 'sorcery/providers/slack' require 'sorcery/providers/wechat' + require 'sorcery/providers/qq' require 'sorcery/providers/microsoft' require 'sorcery/providers/instagram' require 'sorcery/providers/auth0' diff --git a/lib/sorcery/providers/qq.rb b/lib/sorcery/providers/qq.rb new file mode 100644 index 00000000..c4b9f733 --- /dev/null +++ b/lib/sorcery/providers/qq.rb @@ -0,0 +1,76 @@ +module Sorcery + module Providers + # This class adds support for OAuth with graph.qq.com. + # + class Qq < Base + include Protocols::Oauth2 + + attr_accessor :auth_url, :scope, :token_url, :user_info_path, :openid_path + + def initialize + super + + @scope = 'get_user_info' + @auth_url = 'https://graph.qq.com/oauth2.0/authorize' + @openid_path = 'https://graph.qq.com/oauth2.0/me' + @user_info_path = 'https://graph.qq.com/user/get_user_info' + @token_url = 'https://graph.qq.com/oauth2.0/token' + @state = SecureRandom.hex(16) + @grant_type = 'authorization_code' + end + + def authorize_url(options = {}) + oauth_params = { + response_type: 'code', + client_id: @key, + redirect_uri: @callback_url, + state: @state, + scope: @scope + } + "#{options[:authorize_url]}?#{oauth_params.to_query}#qq_redirect" + end + + def get_user_hash(access_token) + openid_response = access_token.get(openid_path, params: { + access_token: access_token.token, + }) + + openid = JSON.parse(openid_response.body).fetch('openid') + + info_response = access_token.get(user_info_path, params: { + access_token: access_token.token, + openid: openid + }) + + {}.tap do |h| + h[:user_info] = JSON.parse(info_response.body) + h[:uid] = openid + end + end + + def get_access_token(args, options = {}) + client = build_client(options) + client.auth_code.get_token( + args[:code], + { client_id: @key, client_secret: @secret, grant_type: @grant_type, redirect_uri: @callback_url}, + options + ) + end + + def login_url(_params, _session) + authorize_url authorize_url: auth_url + end + + def process_callback(params, _session) + args = {}.tap do |a| + a[:code] = params[:code] if params[:code].present? + end + + get_access_token( + args, + token_url: token_url, + ) + end + end + end +end diff --git a/spec/controllers/controller_oauth2_spec.rb b/spec/controllers/controller_oauth2_spec.rb index 0732c3b4..8f38ba8b 100644 --- a/spec/controllers/controller_oauth2_spec.rb +++ b/spec/controllers/controller_oauth2_spec.rb @@ -164,7 +164,7 @@ expect(flash[:notice]).to eq 'Success!' end - %i[github google liveid vk salesforce paypal slack wechat microsoft instagram auth0 discord battlenet].each do |provider| + %i[github google liveid vk salesforce paypal slack wechat microsoft instagram auth0 discord battlenet qq].each do |provider| describe "with #{provider}" do it 'login_at redirects correctly' do get :"login_at_test_#{provider}" @@ -228,6 +228,7 @@ line discord battlenet + qq ] ) @@ -278,6 +279,22 @@ sorcery_controller_external_property_set(:battlenet, :key, '4c43d4862c774ca5bbde89873bf0d338') sorcery_controller_external_property_set(:battlenet, :secret, 'TxY7IwKOykACd8kUxPyVGTqBs44UBDdX') sorcery_controller_external_property_set(:battlenet, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:qq, :key, '4c43d4862c774ca5bbde89873bf0d338') + sorcery_controller_external_property_set(:qq, :secret, 'TxY7IwKOykACd8kUxPyVGTqBs44UBDdX') + sorcery_controller_external_property_set(:qq, :callback_url, 'http://blabla.com') + if SORCERY_ORM == :active_record + ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/activation") + end + + sorcery_reload!([:user_activation,:external], :user_activation_mailer => ::SorceryMailer) + sorcery_controller_property_set(:external_providers, [:facebook, :github, :google, :liveid, :vk, :salesforce, :paypal, :slack, :wechat, :microsoft, :qq]) + end + + after(:all) do + if SORCERY_ORM == :active_record + ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/external") + ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/activation") + end end after(:each) do @@ -300,7 +317,7 @@ expect(ActionMailer::Base.deliveries.size).to eq old_size end - %i[github google liveid vk salesforce paypal wechat microsoft instagram auth0 discord battlenet].each do |provider| + %i[github google liveid vk salesforce paypal wechat microsoft instagram auth0 discord battlenet qq].each do |provider| it "does not send activation email to external users (#{provider})" do old_size = ActionMailer::Base.deliveries.size create_new_external_user provider @@ -363,7 +380,7 @@ let(:user) { double('user', id: 42) } - %w[facebook github google liveid vk salesforce slack discord battlenet].each do |provider| + %w[facebook github google liveid vk salesforce slack discord battlenet qq].each do |provider| context "when #{provider}" do before(:each) do sorcery_model_property_set(:authentications_class, Authentication) @@ -456,6 +473,8 @@ def stub_all_oauth2_requests! }, # response for wechat auth 'unionid' => '123', + # response for qq auth + 'openid' => '123', # response for instagram 'data' => { 'username' => 'pnmahoney', @@ -498,6 +517,7 @@ def set_external_property line discord battlenet + qq ] ) sorcery_controller_external_property_set(:facebook, :key, 'eYVNBjBDi33aa9GkA3w') @@ -546,6 +566,9 @@ def set_external_property sorcery_controller_external_property_set(:battlenet, :key, '4c43d4862c774ca5bbde89873bf0d338') sorcery_controller_external_property_set(:battlenet, :secret, 'TxY7IwKOykACd8kUxPyVGTqBs44UBDdX') sorcery_controller_external_property_set(:battlenet, :callback_url, 'http://blabla.com') + sorcery_controller_external_property_set(:qq, :key, "eYVNBjBDi33aa9GkA3w") + sorcery_controller_external_property_set(:qq, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8") + sorcery_controller_external_property_set(:qq, :callback_url, "http://blabla.com") end def provider_url(provider) @@ -562,7 +585,8 @@ def provider_url(provider) instagram: "https://api.instagram.com/oauth/authorize?client_id=#{::Sorcery::Controller::Config.instagram.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=#{::Sorcery::Controller::Config.instagram.scope}&state", auth0: "https://sorcery-test.auth0.com/authorize?client_id=#{::Sorcery::Controller::Config.auth0.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=openid+profile+email&state", discord: "https://discordapp.com/api/oauth2/authorize?client_id=#{::Sorcery::Controller::Config.discord.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=identify&state", - battlenet: "https://eu.battle.net/oauth/authorize?client_id=#{::Sorcery::Controller::Config.battlenet.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=openid&state" + battlenet: "https://eu.battle.net/oauth/authorize?client_id=#{::Sorcery::Controller::Config.battlenet.key}&display&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=openid&state", + qq: "https://graph.qq.com/oauth2.0/authorize?client_id=#{::Sorcery::Controller::Config.wechat.key}&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=get_user_info&state=#qq_redirect", }[provider] end end diff --git a/spec/rails_app/app/controllers/sorcery_controller.rb b/spec/rails_app/app/controllers/sorcery_controller.rb index 8214e36a..c5ff0836 100644 --- a/spec/rails_app/app/controllers/sorcery_controller.rb +++ b/spec/rails_app/app/controllers/sorcery_controller.rb @@ -126,6 +126,10 @@ def login_at_test_microsoft login_at(:microsoft) end + def login_at_test_qq + login_at(:qq) + end + def login_at_test_google login_at(:google) end @@ -216,6 +220,14 @@ def test_login_from_wechat end end + def test_login_from_qq + if @user = login_from(:qq) + redirect_to 'bla', notice: 'Success!' + else + redirect_to 'blu', alert: 'Failed!' + end + end + def test_login_from_microsoft if (@user = login_from(:microsoft)) redirect_to 'bla', notice: 'Success!' @@ -370,6 +382,14 @@ def test_return_to_with_external_microsoft end end + def test_return_to_with_external_qq + if @user = login_from(:qq) + redirect_back_or_to 'bla', notice: 'Success!' + else + redirect_to 'blu', alert: 'Failed!' + end + end + def test_return_to_with_external_google if (@user = login_from(:google)) redirect_back_or_to 'bla', notice: 'Success!' diff --git a/spec/rails_app/config/routes.rb b/spec/rails_app/config/routes.rb index 4bf82c29..8900e069 100644 --- a/spec/rails_app/config/routes.rb +++ b/spec/rails_app/config/routes.rb @@ -24,6 +24,7 @@ get :test_login_from_github get :test_login_from_paypal get :test_login_from_wechat + get :test_login_from_qq get :test_login_from_microsoft get :test_login_from_google get :test_login_from_liveid @@ -42,6 +43,7 @@ get :login_at_test_github get :login_at_test_paypal get :login_at_test_wechat + get :login_at_test_qq get :login_at_test_microsoft get :login_at_test_google get :login_at_test_liveid @@ -60,6 +62,7 @@ get :test_return_to_with_external_github get :test_return_to_with_external_paypal get :test_return_to_with_external_wechat + get :test_return_to_with_external_qq get :test_return_to_with_external_microsoft get :test_return_to_with_external_google get :test_return_to_with_external_liveid From 9e466cfdb5b01c4a2dbc4f3b1d89548d52f3d2bf Mon Sep 17 00:00:00 2001 From: bealking Date: Sat, 7 Oct 2017 23:11:54 +0800 Subject: [PATCH 110/112] bug fix --- lib/generators/sorcery/templates/initializer.rb | 1 + lib/sorcery/providers/qq.rb | 15 +++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/generators/sorcery/templates/initializer.rb b/lib/generators/sorcery/templates/initializer.rb index 6dbbaea3..a026ff90 100644 --- a/lib/generators/sorcery/templates/initializer.rb +++ b/lib/generators/sorcery/templates/initializer.rb @@ -161,6 +161,7 @@ # config.qq.key = "" # config.qq.secret = "" # config.qq.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=qq" + # config.qq.user_info_mapping = {:username => "nickname"} # # config.google.key = "" # config.google.secret = "" diff --git a/lib/sorcery/providers/qq.rb b/lib/sorcery/providers/qq.rb index c4b9f733..64c0ca17 100644 --- a/lib/sorcery/providers/qq.rb +++ b/lib/sorcery/providers/qq.rb @@ -5,6 +5,7 @@ module Providers class Qq < Base include Protocols::Oauth2 + attr_reader :parse attr_accessor :auth_url, :scope, :token_url, :user_info_path, :openid_path def initialize @@ -15,8 +16,8 @@ def initialize @openid_path = 'https://graph.qq.com/oauth2.0/me' @user_info_path = 'https://graph.qq.com/user/get_user_info' @token_url = 'https://graph.qq.com/oauth2.0/token' + @parse = :query @state = SecureRandom.hex(16) - @grant_type = 'authorization_code' end def authorize_url(options = {}) @@ -32,27 +33,29 @@ def authorize_url(options = {}) def get_user_hash(access_token) openid_response = access_token.get(openid_path, params: { - access_token: access_token.token, + access_token: access_token.token }) - openid = JSON.parse(openid_response.body).fetch('openid') + openid = openid_response.body.match(/"openid":"(\w{3,32})"/) || [nil, ''] info_response = access_token.get(user_info_path, params: { access_token: access_token.token, - openid: openid + oauth_consumer_key: @key, + openid: openid[1] }) {}.tap do |h| h[:user_info] = JSON.parse(info_response.body) - h[:uid] = openid + h[:uid] = openid[1] end end def get_access_token(args, options = {}) client = build_client(options) + client.auth_code.get_token( args[:code], - { client_id: @key, client_secret: @secret, grant_type: @grant_type, redirect_uri: @callback_url}, + { client_id: @key, client_secret: @secret, redirect_uri: @callback_url, parse: @parse}, options ) end From 4a7cd0e76f9bdde0512ac6505d84ab3c34f7747e Mon Sep 17 00:00:00 2001 From: bealking Date: Mon, 30 Aug 2021 22:42:39 +0800 Subject: [PATCH 111/112] solve conflict --- spec/controllers/controller_oauth2_spec.rb | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/spec/controllers/controller_oauth2_spec.rb b/spec/controllers/controller_oauth2_spec.rb index 8f38ba8b..c538e9ed 100644 --- a/spec/controllers/controller_oauth2_spec.rb +++ b/spec/controllers/controller_oauth2_spec.rb @@ -282,19 +282,6 @@ sorcery_controller_external_property_set(:qq, :key, '4c43d4862c774ca5bbde89873bf0d338') sorcery_controller_external_property_set(:qq, :secret, 'TxY7IwKOykACd8kUxPyVGTqBs44UBDdX') sorcery_controller_external_property_set(:qq, :callback_url, 'http://blabla.com') - if SORCERY_ORM == :active_record - ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/activation") - end - - sorcery_reload!([:user_activation,:external], :user_activation_mailer => ::SorceryMailer) - sorcery_controller_property_set(:external_providers, [:facebook, :github, :google, :liveid, :vk, :salesforce, :paypal, :slack, :wechat, :microsoft, :qq]) - end - - after(:all) do - if SORCERY_ORM == :active_record - ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/external") - ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/activation") - end end after(:each) do From 2299885081b4c133fa929908f64c3113bf382f31 Mon Sep 17 00:00:00 2001 From: bealking Date: Fri, 7 Jun 2024 23:39:17 +0800 Subject: [PATCH 112/112] =?UTF-8?q?=E5=85=BC=E5=AE=B9Rails7=E7=9A=84?= =?UTF-8?q?=E5=A4=96=E9=93=BE=E8=B7=B3=E8=BD=AC=E4=BF=9D=E6=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/sorcery/controller/submodules/external.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sorcery/controller/submodules/external.rb b/lib/sorcery/controller/submodules/external.rb index 9b67b6e3..54074c42 100644 --- a/lib/sorcery/controller/submodules/external.rb +++ b/lib/sorcery/controller/submodules/external.rb @@ -119,7 +119,7 @@ def sorcery_fixup_callback_url(provider) # sends user to authenticate at the provider's website. # after authentication the user is redirected to the callback defined in the provider config def login_at(provider_name, args = {}) - redirect_to sorcery_login_url(provider_name, args) + redirect_to sorcery_login_url(provider_name, args), allow_other_host: true end # tries to login the user from provider's callback