From a4479106a3ac855e63b4baaf876f6e47ab533a8c Mon Sep 17 00:00:00 2001 From: Gregorius Marco <gmarco@gitlab.com> Date: Tue, 7 Jan 2025 05:20:43 +0000 Subject: [PATCH] Support loading session from ActionDispatch::Session::CacheStore --- config/initializers/session_store.rb | 5 +- lib/gitlab/sessions/redis_store_serializer.rb | 24 +++++++ spec/initializers/session_store_spec.rb | 5 +- .../sessions/redis_store_serializer_spec.rb | 63 +++++++++++++++++++ 4 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 lib/gitlab/sessions/redis_store_serializer.rb create mode 100644 spec/lib/gitlab/sessions/redis_store_serializer_spec.rb diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index c75f314e51895..8cc8c39ac7406 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -36,7 +36,10 @@ Rails.application.configure do config.session_store( Gitlab::Sessions::RedisStore, # Using the cookie_store would enable session replay attacks - redis_server: Gitlab::Redis::Sessions.params.merge(namespace: Gitlab::Redis::Sessions::SESSION_NAMESPACE), + redis_server: Gitlab::Redis::Sessions.params.merge( + namespace: Gitlab::Redis::Sessions::SESSION_NAMESPACE, + serializer: Gitlab::Sessions::RedisStoreSerializer + ), key: cookie_key, secure: Gitlab.config.gitlab.https, httponly: true, diff --git a/lib/gitlab/sessions/redis_store_serializer.rb b/lib/gitlab/sessions/redis_store_serializer.rb new file mode 100644 index 0000000000000..8d0cba26d9248 --- /dev/null +++ b/lib/gitlab/sessions/redis_store_serializer.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Gitlab + module Sessions + module RedisStoreSerializer + def self.load(val) + deserialized = Marshal.load(val) # rubocop:disable Security/MarshalLoad -- We're loading session data similar to Redis::Store::Serialization#get from redis-store gem + + return deserialized if deserialized.is_a?(Hash) + + # Session data can be an instance of ActiveSupport::Cache::Entry + # when we're using session store based on ActionDispatch::Session::CacheStore + # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/176108 + deserialized&.value + rescue StandardError => e + Gitlab::ErrorTracking.track_and_raise_exception(e) + end + + def self.dump(val) + Marshal.dump(val) + end + end + end +end diff --git a/spec/initializers/session_store_spec.rb b/spec/initializers/session_store_spec.rb index 8b8ac0eea71ec..917f7c535f55a 100644 --- a/spec/initializers/session_store_spec.rb +++ b/spec/initializers/session_store_spec.rb @@ -18,7 +18,10 @@ expect(subject).to receive(:session_store).with( Gitlab::Sessions::RedisStore, a_hash_including( - redis_server: Gitlab::Redis::Sessions.params.merge(namespace: Gitlab::Redis::Sessions::SESSION_NAMESPACE) + redis_server: Gitlab::Redis::Sessions.params.merge( + namespace: Gitlab::Redis::Sessions::SESSION_NAMESPACE, + serializer: Gitlab::Sessions::RedisStoreSerializer + ) ) ) diff --git a/spec/lib/gitlab/sessions/redis_store_serializer_spec.rb b/spec/lib/gitlab/sessions/redis_store_serializer_spec.rb new file mode 100644 index 0000000000000..430fe069e0f11 --- /dev/null +++ b/spec/lib/gitlab/sessions/redis_store_serializer_spec.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Sessions::RedisStoreSerializer, feature_category: :system_access do + let(:hash) { { a: 1, b: 2 } } + let(:serialized) { Marshal.dump(val) } + + describe '.load' do + shared_examples 'unmarshal' do + it 'returns original value' do + expect(load).to eq(expected) + end + end + + subject(:load) { described_class.load(serialized) } + + context 'with hash value' do + let(:val) { hash } + let(:expected) { hash } + + it_behaves_like 'unmarshal' + end + + context 'with ActiveSupport::Cache::Entry value' do + let(:val) { ActiveSupport::Cache::Entry.new(hash) } + let(:expected) { hash } + + it_behaves_like 'unmarshal' + end + + context 'with nil value' do + let(:val) { nil } + let(:expected) { nil } + + it_behaves_like 'unmarshal' + end + + context 'with unrecognized type' do + let(:val) { %w[a b c] } + + it 'tracks and raises an exception' do + expect(Gitlab::ErrorTracking).to receive(:track_and_raise_exception).with(instance_of(NoMethodError)) + + load + end + end + end + + describe '.dump' do + subject(:dump) { described_class.dump(hash) } + + it 'calls Marshal.dump' do + expect(Marshal).to receive(:dump).with(hash) + + dump + end + + it 'returns marshalled object' do + expect(dump).to eq(Marshal.dump(hash)) + end + end +end -- GitLab