Follow up - ca56de3648
- Maintenance: Updated to Rails 6.0.4 and the new Zeitwerk autoloader: Fixed deprecation of autoloading in initializers causing namespace lookup errors.
This commit is contained in:
parent
3d946cf362
commit
887b779b40
17 changed files with 194 additions and 68 deletions
|
@ -2,4 +2,17 @@
|
||||||
|
|
||||||
class Session < ActiveRecord::SessionStore::Session
|
class Session < ActiveRecord::SessionStore::Session
|
||||||
include Session::SetsPersistentFlag
|
include Session::SetsPersistentFlag
|
||||||
|
|
||||||
|
def self.secure_flag?
|
||||||
|
# enable runtime change support in test/develop environments
|
||||||
|
return https? if !Rails.env.production?
|
||||||
|
|
||||||
|
@secure_flag ||= https?
|
||||||
|
rescue ActiveRecord::NoDatabaseError, ActiveRecord::StatementInvalid
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.https?
|
||||||
|
Setting.get('http_type') == 'https'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
# Rails' constant auto-loading resolves to 'rails/initializable' instead
|
# Rails' constant auto-loading resolves to 'rails/initializable' instead
|
||||||
require_dependency 'zammad/application/initializer/db_preflight_check'
|
require 'zammad/application/initializer/db_preflight_check'
|
||||||
|
|
||||||
Zammad::Application::Initializer::DbPreflightCheck.perform
|
Zammad::Application::Initializer::DbPreflightCheck.perform
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
return if !ActiveRecord::Base.connected?
|
Rails.application.reloader.to_prepare do
|
||||||
|
|
||||||
# sync logo to fs / only if settings already exists
|
next if !ActiveRecord::Base.connected?
|
||||||
return if ActiveRecord::Base.connection.tables.exclude?('settings')
|
|
||||||
return if Setting.column_names.exclude?('state_current')
|
|
||||||
|
|
||||||
StaticAssets.sync
|
# sync logo to fs / only if settings already exists
|
||||||
|
next if ActiveRecord::Base.connection.tables.exclude?('settings')
|
||||||
|
next if Setting.column_names.exclude?('state_current')
|
||||||
|
|
||||||
|
StaticAssets.sync
|
||||||
|
end
|
||||||
|
|
|
@ -2,10 +2,12 @@
|
||||||
|
|
||||||
# update settings for searchable models
|
# update settings for searchable models
|
||||||
|
|
||||||
begin
|
Rails.application.reloader.to_prepare do
|
||||||
return if !Setting.exists?(name: 'models_searchable')
|
begin
|
||||||
|
next if !Setting.exists?(name: 'models_searchable')
|
||||||
|
|
||||||
Setting.set('models_searchable', Models.searchable.map(&:to_s))
|
Setting.set('models_searchable', Models.searchable.map(&:to_s))
|
||||||
rescue ActiveRecord::StatementInvalid
|
rescue ActiveRecord::StatementInvalid
|
||||||
nil
|
nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
# Rails' constant auto-loading resolves to 'rails/initializable' instead
|
# Rails' constant auto-loading resolves to 'rails/initializable' instead
|
||||||
require_dependency 'zammad/application/initializer/session_store'
|
require 'zammad/application/initializer/session_store'
|
||||||
|
|
||||||
Zammad::Application::Initializer::SessionStore.perform
|
Zammad::Application::Initializer::SessionStore.perform
|
||||||
|
|
17
lib/core_ext/action_dispatch/middleware/cookies.rb
Normal file
17
lib/core_ext/action_dispatch/middleware/cookies.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
require 'action_dispatch/middleware/cookies'
|
||||||
|
|
||||||
|
module ActionDispatch
|
||||||
|
class Cookies
|
||||||
|
class CookieJar
|
||||||
|
|
||||||
|
alias original_write_cookie? write_cookie?
|
||||||
|
|
||||||
|
# https://github.com/rails/rails/blob/v6.0.4/actionpack/lib/action_dispatch/middleware/cookies.rb#L447-L449
|
||||||
|
def write_cookie?(cookie)
|
||||||
|
original_write_cookie?(cookie.merge(secure: ::Session.secure_flag?))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
20
lib/core_ext/rack/session/abstract/id.rb
Normal file
20
lib/core_ext/rack/session/abstract/id.rb
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
require 'rack/session/abstract/id'
|
||||||
|
|
||||||
|
module Rack
|
||||||
|
module Session
|
||||||
|
module Abstract
|
||||||
|
class Persisted
|
||||||
|
|
||||||
|
alias original_security_matches? security_matches?
|
||||||
|
|
||||||
|
# https://github.com/rack/rack/blob/2.2.3/lib/rack/session/abstract/id.rb#L363-L366
|
||||||
|
def security_matches?(request, options)
|
||||||
|
options[:secure] = ::Session.secure_flag?
|
||||||
|
original_security_matches?(request, options)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
22
lib/core_ext/rack/utils.rb
Normal file
22
lib/core_ext/rack/utils.rb
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
||||||
|
|
||||||
|
require 'rack/utils'
|
||||||
|
|
||||||
|
module Rack
|
||||||
|
module Utils
|
||||||
|
|
||||||
|
module_function
|
||||||
|
|
||||||
|
singleton_class.alias_method :original_add_cookie_to_header, :add_cookie_to_header
|
||||||
|
|
||||||
|
# https://github.com/rack/rack/blob/2.2.3/lib/rack/session/utils.rb#L223-L262
|
||||||
|
def add_cookie_to_header(header, key, value)
|
||||||
|
|
||||||
|
if value.is_a?(Hash)
|
||||||
|
value[:secure] = ::Session.secure_flag?
|
||||||
|
end
|
||||||
|
|
||||||
|
original_add_cookie_to_header(header, key, value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -7,7 +7,7 @@ require 'zammad/application/initializer/db_preflight_check/nulldb'
|
||||||
|
|
||||||
module Zammad
|
module Zammad
|
||||||
class Application
|
class Application
|
||||||
class Initializer
|
module Initializer
|
||||||
module DbPreflightCheck
|
module DbPreflightCheck
|
||||||
def self.perform
|
def self.perform
|
||||||
adapter.perform
|
adapter.perform
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
module Zammad
|
module Zammad
|
||||||
class Application
|
class Application
|
||||||
class Initializer
|
module Initializer
|
||||||
module DbPreflightCheck
|
module DbPreflightCheck
|
||||||
module Base
|
module Base
|
||||||
def check_version_compatibility
|
def check_version_compatibility
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
module Zammad
|
module Zammad
|
||||||
class Application
|
class Application
|
||||||
class Initializer
|
module Initializer
|
||||||
module DbPreflightCheck
|
module DbPreflightCheck
|
||||||
module Mysql2
|
module Mysql2
|
||||||
extend Base
|
extend Base
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
module Zammad
|
module Zammad
|
||||||
class Application
|
class Application
|
||||||
class Initializer
|
module Initializer
|
||||||
module DbPreflightCheck
|
module DbPreflightCheck
|
||||||
module Nulldb
|
module Nulldb
|
||||||
# no-op
|
# no-op
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
module Zammad
|
module Zammad
|
||||||
class Application
|
class Application
|
||||||
class Initializer
|
module Initializer
|
||||||
module DbPreflightCheck
|
module DbPreflightCheck
|
||||||
module Postgresql
|
module Postgresql
|
||||||
extend Base
|
extend Base
|
||||||
|
|
|
@ -6,24 +6,28 @@
|
||||||
|
|
||||||
module Zammad
|
module Zammad
|
||||||
class Application
|
class Application
|
||||||
class Initializer
|
module Initializer
|
||||||
module SessionStore
|
module SessionStore
|
||||||
STORE_TYPE = :active_record_store # default: :cookie_store
|
STORE_TYPE = :active_record_store # default: :cookie_store
|
||||||
SESSION_KEY = "_zammad_session_#{Digest::MD5.hexdigest(Rails.root.to_s)[5..15]}".freeze # default: '_zammad_session'
|
SESSION_KEY = "_zammad_session_#{Digest::MD5.hexdigest(Rails.root.to_s)[5..15]}".freeze # default: '_zammad_session'
|
||||||
|
|
||||||
def self.perform
|
def self.perform
|
||||||
ActionDispatch::Session::ActiveRecordStore.session_class = Session
|
# it's important to register the session store at initialization time
|
||||||
|
# otherwise the store won't be used
|
||||||
|
# ATTENTION: Rails/Rack Cookie handling was customized to call `Session.secure_flag?`
|
||||||
|
# instead of accessing the `:secure` key (default Rack/Rails behavior).
|
||||||
|
# See: lib/core_ext/action_dispatch/middleware/cookies.rb
|
||||||
|
# See: lib/core_ext/rack/session/abstract/id.rb
|
||||||
|
# See: lib/core_ext/rack/session/utils.rb
|
||||||
Rails.application.config.session_store STORE_TYPE,
|
Rails.application.config.session_store STORE_TYPE,
|
||||||
key: SESSION_KEY,
|
key: SESSION_KEY
|
||||||
secure: secure?
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.secure?
|
# once the application is initialized and we can access the models
|
||||||
Setting.get('http_type') == 'https'
|
# we need to update the session_class
|
||||||
rescue ActiveRecord::StatementInvalid
|
Rails.application.reloader.to_prepare do
|
||||||
false
|
ActionDispatch::Session::ActiveRecordStore.session_class = Session
|
||||||
|
end
|
||||||
end
|
end
|
||||||
private_class_method :secure?
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
|
||||||
|
|
||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe Zammad::Application::Initializer::SessionStore do
|
|
||||||
describe '.perform' do
|
|
||||||
context 'for HTTP deployment' do
|
|
||||||
before { Setting.set('http_type', 'http') }
|
|
||||||
|
|
||||||
# Why not use the "change" matcher in this example?
|
|
||||||
#
|
|
||||||
# This initializer is already run when the application is loaded for testing.
|
|
||||||
# Since the test env always uses http, the :secure option is already set to false.
|
|
||||||
it 'adds { secure: false } to application session options' do
|
|
||||||
described_class.perform
|
|
||||||
|
|
||||||
expect(Rails.application.config.session_options).to include(secure: false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'for HTTPS deployment' do
|
|
||||||
before { Setting.set('http_type', 'https') }
|
|
||||||
|
|
||||||
it 'adds { secure: true } to application session options' do
|
|
||||||
expect { described_class.perform }
|
|
||||||
.to change(Rails.application.config, :session_options)
|
|
||||||
.to include(secure: true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -192,7 +192,7 @@ RSpec.describe 'External Credentials', type: :request do
|
||||||
|
|
||||||
shared_examples 'for failure cases' do
|
shared_examples 'for failure cases' do
|
||||||
it 'responds with the appropriate status and error message' do
|
it 'responds with the appropriate status and error message' do
|
||||||
send(*endpoint, as: :json, params: try(:params) || {})
|
send(*endpoint, as: :json, params: try(:params) || {}, headers: headers)
|
||||||
|
|
||||||
expect(response).to have_http_status(status)
|
expect(response).to have_http_status(status)
|
||||||
expect(json_response).to include('error' => error_message)
|
expect(json_response).to include('error' => error_message)
|
||||||
|
@ -415,7 +415,14 @@ RSpec.describe 'External Credentials', type: :request do
|
||||||
|
|
||||||
let!(:twitter_credential) { create(:twitter_credential) }
|
let!(:twitter_credential) { create(:twitter_credential) }
|
||||||
|
|
||||||
before { get '/api/v1/external_credentials/twitter/link_account', as: :json }
|
# Rails / Rack needs to detect that the request comes via HTTPS as well
|
||||||
|
let(:headers) do
|
||||||
|
{
|
||||||
|
'X-Forwarded-Proto' => 'https'
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
before { get '/api/v1/external_credentials/twitter/link_account', as: :json, headers: headers }
|
||||||
|
|
||||||
include_examples 'for failure cases' do
|
include_examples 'for failure cases' do
|
||||||
let(:status) { :unprocessable_entity }
|
let(:status) { :unprocessable_entity }
|
||||||
|
@ -458,7 +465,14 @@ RSpec.describe 'External Credentials', type: :request do
|
||||||
let(:oauth_token) { 'DyhnyQAAAAAA9CNXAAABcSxAexs' }
|
let(:oauth_token) { 'DyhnyQAAAAAA9CNXAAABcSxAexs' }
|
||||||
let(:oauth_verifier) { '15DOeRkjP4JkOSVqULkTKA1SCuIPP105' }
|
let(:oauth_verifier) { '15DOeRkjP4JkOSVqULkTKA1SCuIPP105' }
|
||||||
|
|
||||||
before { get '/api/v1/external_credentials/twitter/link_account', as: :json }
|
# Rails / Rack needs to detect that the request comes via HTTPS as well
|
||||||
|
let(:headers) do
|
||||||
|
{
|
||||||
|
'X-Forwarded-Proto' => 'https'
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
before { get '/api/v1/external_credentials/twitter/link_account', as: :json, headers: headers }
|
||||||
|
|
||||||
context 'if Twitter account has already been added' do
|
context 'if Twitter account has already been added' do
|
||||||
let!(:channel) { create(:twitter_channel, custom_options: channel_options) }
|
let!(:channel) { create(:twitter_channel, custom_options: channel_options) }
|
||||||
|
@ -472,12 +486,12 @@ RSpec.describe 'External Credentials', type: :request do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'uses the existing channel' do
|
it 'uses the existing channel' do
|
||||||
expect { send(*endpoint, as: :json, params: params) }
|
expect { send(*endpoint, as: :json, params: params, headers: headers) }
|
||||||
.not_to change(Channel, :count)
|
.not_to change(Channel, :count)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates channel properties' do
|
it 'updates channel properties' do
|
||||||
expect { send(*endpoint, as: :json, params: params) }
|
expect { send(*endpoint, as: :json, params: params, headers: headers) }
|
||||||
.to change { channel.reload.options[:user][:name] }
|
.to change { channel.reload.options[:user][:name] }
|
||||||
.and change { channel.reload.options[:auth][:external_credential_id] }
|
.and change { channel.reload.options[:auth][:external_credential_id] }
|
||||||
.and change { channel.reload.options[:auth][:oauth_token] }
|
.and change { channel.reload.options[:auth][:oauth_token] }
|
||||||
|
@ -485,7 +499,7 @@ RSpec.describe 'External Credentials', type: :request do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'subscribes to webhooks' do
|
it 'subscribes to webhooks' do
|
||||||
send(*endpoint, as: :json, params: params)
|
send(*endpoint, as: :json, params: params, headers: headers)
|
||||||
|
|
||||||
expect(WebMock)
|
expect(WebMock)
|
||||||
.to have_requested(:post, "https://api.twitter.com/1.1/account_activity/all/#{twitter_credential.credentials[:env]}/subscriptions.json")
|
.to have_requested(:post, "https://api.twitter.com/1.1/account_activity/all/#{twitter_credential.credentials[:env]}/subscriptions.json")
|
||||||
|
@ -496,7 +510,7 @@ RSpec.describe 'External Credentials', type: :request do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates a new channel' do
|
it 'creates a new channel' do
|
||||||
expect { send(*endpoint, as: :json, params: params) }
|
expect { send(*endpoint, as: :json, params: params, headers: headers) }
|
||||||
.to change(Channel, :count).by(1)
|
.to change(Channel, :count).by(1)
|
||||||
|
|
||||||
expect(Channel.last.options)
|
expect(Channel.last.options)
|
||||||
|
@ -506,19 +520,19 @@ RSpec.describe 'External Credentials', type: :request do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'redirects to the newly created channel' do
|
it 'redirects to the newly created channel' do
|
||||||
send(*endpoint, as: :json, params: params)
|
send(*endpoint, as: :json, params: params, headers: headers)
|
||||||
|
|
||||||
expect(response).to redirect_to(%r{/#channels/twitter/#{Channel.last.id}$})
|
expect(response).to redirect_to(%r{/#channels/twitter/#{Channel.last.id}$})
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'clears the :request_token session variable' do
|
it 'clears the :request_token session variable' do
|
||||||
send(*endpoint, as: :json, params: params)
|
send(*endpoint, as: :json, params: params, headers: headers)
|
||||||
|
|
||||||
expect(session[:request_token]).to be(nil)
|
expect(session[:request_token]).to be(nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'subscribes to webhooks' do
|
it 'subscribes to webhooks' do
|
||||||
send(*endpoint, as: :json, params: params)
|
send(*endpoint, as: :json, params: params, headers: headers)
|
||||||
|
|
||||||
expect(WebMock)
|
expect(WebMock)
|
||||||
.to have_requested(:post, "https://api.twitter.com/1.1/account_activity/all/#{twitter_credential.credentials[:env]}/subscriptions.json")
|
.to have_requested(:post, "https://api.twitter.com/1.1/account_activity/all/#{twitter_credential.credentials[:env]}/subscriptions.json")
|
||||||
|
|
|
@ -4,6 +4,68 @@ require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe 'Sessions endpoints', type: :request do
|
RSpec.describe 'Sessions endpoints', type: :request do
|
||||||
|
|
||||||
|
describe 'GET /' do
|
||||||
|
|
||||||
|
let(:headers) { {} }
|
||||||
|
let(:session_key) { Zammad::Application::Initializer::SessionStore::SESSION_KEY }
|
||||||
|
|
||||||
|
before do
|
||||||
|
Setting.set('http_type', http_type)
|
||||||
|
|
||||||
|
get '/', headers: headers
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when Setting 'http_type' is set to 'https'" do
|
||||||
|
|
||||||
|
let(:http_type) { 'https' }
|
||||||
|
|
||||||
|
context "when it's not an HTTPS request" do
|
||||||
|
|
||||||
|
it 'sets no Cookie' do
|
||||||
|
expect(response.header['Set-Cookie']).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when it's an HTTPS request" do
|
||||||
|
|
||||||
|
let(:headers) do
|
||||||
|
{
|
||||||
|
'X-Forwarded-Proto' => 'https'
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it "sets Cookie with 'secure' flag" do
|
||||||
|
expect(response.header['Set-Cookie']).to include(session_key).and include('; secure;')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when Setting 'http_type' is set to 'http'" do
|
||||||
|
|
||||||
|
let(:http_type) { 'http' }
|
||||||
|
|
||||||
|
context "when it's not an HTTPS request" do
|
||||||
|
|
||||||
|
it 'sets Cookie' do
|
||||||
|
expect(response.header['Set-Cookie']).to include(session_key).and not_include('; secure;')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when it's an HTTPS request" do
|
||||||
|
|
||||||
|
let(:headers) do
|
||||||
|
{
|
||||||
|
'X-Forwarded-Proto' => 'https'
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it "sets Cookie without 'secure' flag" do
|
||||||
|
expect(response.header['Set-Cookie']).to include(session_key).and not_include('; secure;')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'GET /signshow' do
|
describe 'GET /signshow' do
|
||||||
|
|
||||||
context 'user logged in' do
|
context 'user logged in' do
|
||||||
|
|
Loading…
Reference in a new issue