Commit 229fd8af authored by nmilojevic1's avatar nmilojevic1

Add specs for both SharedState and Sessions store

parent e6551492
...@@ -45,22 +45,26 @@ RSpec.describe Groups::DependencyProxyForContainersController do ...@@ -45,22 +45,26 @@ RSpec.describe Groups::DependencyProxyForContainersController do
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:not_found)
end end
context 'with an active session', :clean_gitlab_redis_shared_state do shared_examples 'active session' do
let(:session_id) { '42' } context 'with an active session' do
let(:session_time) { 5.minutes.ago } let(:session_id) { '42' }
let(:stored_session) do let(:session_time) { 5.minutes.ago }
{ 'active_group_sso_sign_ins' => { saml_provider.id => session_time } } let(:stored_session) do
end { 'active_group_sso_sign_ins' => { saml_provider.id => session_time } }
end
before do before do
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session)) redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session))
redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id]) redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id])
end
end end
end
it_behaves_like successful_example it_behaves_like successful_example
end
end end
it_behaves_like 'redis sessions store', 'active session'
end end
context 'when git check is not enforced' do context 'when git check is not enforced' do
......
...@@ -85,44 +85,52 @@ RSpec.describe 'Login' do ...@@ -85,44 +85,52 @@ RSpec.describe 'Login' do
expect(page.body).to have_link('Register now', href: new_user_registration_path) expect(page.body).to have_link('Register now', href: new_user_registration_path)
end end
describe 'with two-factor authentication required', :clean_gitlab_redis_shared_state do RSpec.shared_examples_for 'two-factor authentication' do
let_it_be(:user) { create(:user) }
let_it_be(:smartcard_identity) { create(:smartcard_identity, user: user) }
before do before do
stub_application_setting(require_two_factor_authentication: true) load Rails.root.join('config/initializers/session_store.rb')
end end
context 'with a smartcard session' do describe 'with two-factor authentication required' do
let(:openssl_certificate_store) { instance_double(OpenSSL::X509::Store) } let_it_be(:user) { create(:user) }
let(:openssl_certificate) do let_it_be(:smartcard_identity) { create(:smartcard_identity, user: user) }
instance_double(OpenSSL::X509::Certificate, subject: smartcard_identity.subject, issuer: smartcard_identity.issuer)
before do
stub_application_setting(require_two_factor_authentication: true)
end end
it 'does not ask for Two-Factor Authentication' do context 'with a smartcard session' do
allow(Gitlab::Auth::Smartcard::Certificate).to receive(:store).and_return(openssl_certificate_store) let(:openssl_certificate_store) { instance_double(OpenSSL::X509::Store) }
allow(OpenSSL::X509::Certificate).to receive(:new).and_return(openssl_certificate) let(:openssl_certificate) do
allow(openssl_certificate_store).to receive(:verify).and_return(true) instance_double(OpenSSL::X509::Certificate, subject: smartcard_identity.subject, issuer: smartcard_identity.issuer)
end
it 'does not ask for Two-Factor Authentication' do
allow(Gitlab::Auth::Smartcard::Certificate).to receive(:store).and_return(openssl_certificate_store)
allow(OpenSSL::X509::Certificate).to receive(:new).and_return(openssl_certificate)
allow(openssl_certificate_store).to receive(:verify).and_return(true)
# Loging using smartcard # Loging using smartcard
visit verify_certificate_smartcard_path(client_certificate: openssl_certificate) visit verify_certificate_smartcard_path(client_certificate: openssl_certificate)
visit profile_path visit profile_path
expect(page).not_to have_content('Two-Factor Authentication') expect(page).not_to have_content('Two-Factor Authentication')
end
end end
end
context 'without a smartcard session' do context 'without a smartcard session' do
it 'asks for Two-Factor Authentication' do it 'asks for Two-Factor Authentication' do
sign_in(user) sign_in(user)
visit profile_path visit profile_path
expect(page).to have_content('Two-Factor Authentication') expect(page).to have_content('Two-Factor Authentication')
end
end end
end end
end end
it_behaves_like 'redis sessions store', 'two-factor authentication'
end end
end end
end end
......
...@@ -2,62 +2,66 @@ ...@@ -2,62 +2,66 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::Auth::Otp::SessionEnforcer, :clean_gitlab_redis_shared_state do RSpec.describe Gitlab::Auth::Otp::SessionEnforcer do
let_it_be(:key) { create(:key)} shared_examples_for 'otp session enforcer' do
let_it_be(:key) { create(:key)}
describe '#update_session' do describe '#update_session' do
let(:redis) { double(:redis) } let(:redis) { double(:redis) }
before do
stub_licensed_features(git_two_factor_enforcement: true)
end
it 'registers a session in Redis' do
expect(Gitlab::Redis::SharedState).to receive(:with).and_yield(redis)
session_expiry_in_seconds = Gitlab::CurrentSettings.git_two_factor_session_expiry.minutes.to_i
expect(redis).to(
receive(:setex)
.with("#{described_class::OTP_SESSIONS_NAMESPACE}:#{key.id}",
session_expiry_in_seconds,
true)
.once)
described_class.new(key).update_session
end
context 'when licensed feature is not available' do
before do before do
stub_licensed_features(git_two_factor_enforcement: false) stub_licensed_features(git_two_factor_enforcement: true)
end end
it 'does not register a session in Redis' do it 'registers a session in Redis' do
expect(redis).not_to receive(:setex) expect(redis_store_class).to receive(:with).and_yield(redis)
session_expiry_in_seconds = Gitlab::CurrentSettings.git_two_factor_session_expiry.minutes.to_i
expect(redis).to(
receive(:setex)
.with("#{::Gitlab::Redis::Sessions::OTP_SESSIONS_NAMESPACE}:#{key.id}",
session_expiry_in_seconds,
true)
.once)
described_class.new(key).update_session described_class.new(key).update_session
end end
end
end
describe '#access_restricted?' do context 'when licensed feature is not available' do
subject { described_class.new(key).access_restricted? } before do
stub_licensed_features(git_two_factor_enforcement: false)
end
it 'does not register a session in Redis' do
expect(redis).not_to receive(:setex)
before do described_class.new(key).update_session
stub_licensed_features(git_two_factor_enforcement: true) end
end
end end
context 'with existing session' do describe '#access_restricted?' do
subject { described_class.new(key).access_restricted? }
before do before do
Gitlab::Redis::SharedState.with do |redis| stub_licensed_features(git_two_factor_enforcement: true)
redis.set("#{described_class::OTP_SESSIONS_NAMESPACE}:#{key.id}", true )
end
end end
it { is_expected.to be_falsey } context 'with existing session' do
end before do
redis_store_class.with do |redis|
redis.set("#{::Gitlab::Redis::Sessions::OTP_SESSIONS_NAMESPACE}:#{key.id}", true )
end
end
it { is_expected.to be_falsey }
end
context 'without an existing session' do context 'without an existing session' do
it { is_expected.to be_truthy } it { is_expected.to be_truthy }
end
end end
end end
it_behaves_like 'redis sessions store', 'otp session enforcer'
end end
...@@ -29,22 +29,26 @@ RSpec.describe Gitlab::Auth::Smartcard::SessionEnforcer do ...@@ -29,22 +29,26 @@ RSpec.describe Gitlab::Auth::Smartcard::SessionEnforcer do
stub_smartcard_setting(enabled: true, required_for_git_access: true) stub_smartcard_setting(enabled: true, required_for_git_access: true)
end end
context 'with a smartcard session', :clean_gitlab_redis_shared_state do RSpec.shared_examples_for 'smartcard session' do
let(:session_id) { '42' } context 'with a smartcard session' do
let(:stored_session) do let(:session_id) { '42' }
{ 'smartcard_signins' => { 'last_signin_at' => 5.minutes.ago } } let(:stored_session) do
end { 'smartcard_signins' => { 'last_signin_at' => 5.minutes.ago } }
end
before do before do
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session)) redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session))
redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id]) redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id])
end
end end
end
it { is_expected.to be_falsey } it { is_expected.to be_falsey }
end
end end
it_behaves_like 'redis sessions store', 'smartcard session'
context 'without any session' do context 'without any session' do
it { is_expected.to be_truthy } it { is_expected.to be_truthy }
end end
......
...@@ -3,45 +3,49 @@ ...@@ -3,45 +3,49 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::Auth::Smartcard::Session do RSpec.describe Gitlab::Auth::Smartcard::Session do
describe '#active?' do RSpec.shared_examples_for 'smartcard session' do
let(:user) { create(:user) } describe '#active?' do
let(:user) { create(:user) }
subject { described_class.new.active?(user) } subject { described_class.new.active?(user) }
context 'with a smartcard session', :clean_gitlab_redis_shared_state do context 'with a smartcard session' do
let(:session_id) { '42' } let(:session_id) { '42' }
let(:stored_session) do let(:stored_session) do
{ 'smartcard_signins' => { 'last_signin_at' => 5.minutes.ago } } { 'smartcard_signins' => { 'last_signin_at' => 5.minutes.ago } }
end end
before do before do
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session)) redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session))
redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id]) redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id])
end
end end
it { is_expected.to be_truthy }
end end
it { is_expected.to be_truthy } context 'without any session' do
it { is_expected.to be_falsey }
end
end end
context 'without any session' do describe '#update_active' do
it { is_expected.to be_falsey } let(:now) { Time.now }
end
end
describe '#update_active' do around do |example|
let(:now) { Time.now } Gitlab::Session.with_session({}) do
example.run
around do |example| end
Gitlab::Session.with_session({}) do
example.run
end end
end
it 'stores the time of last sign-in' do it 'stores the time of last sign-in' do
subject.update_active(now) subject.update_active(now)
expect(Gitlab::Session.current[:smartcard_signins]).to eq({ 'last_signin_at' => now }) expect(Gitlab::Session.current[:smartcard_signins]).to eq({ 'last_signin_at' => now })
end
end end
end end
it_behaves_like 'redis sessions store', 'smartcard session'
end end
This diff is collapsed.
...@@ -154,26 +154,30 @@ RSpec.describe API::Internal::Base do ...@@ -154,26 +154,30 @@ RSpec.describe API::Internal::Base do
project.add_developer(user) project.add_developer(user)
end end
context 'user with a smartcard session', :clean_gitlab_redis_shared_state do RSpec.shared_examples_for 'smartcard session' do
let(:session_id) { '42' } context 'user with a smartcard session' do
let(:stored_session) do let(:session_id) { '42' }
{ 'smartcard_signins' => { 'last_signin_at' => 5.minutes.ago } } let(:stored_session) do
end { 'smartcard_signins' => { 'last_signin_at' => 5.minutes.ago } }
end
before do before do
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session)) redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session))
redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id]) redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id])
end
end end
end
it "allows access" do it "allows access" do
subject subject
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
end
end end
end end
it_behaves_like 'redis sessions store', 'smartcard session'
context 'user without a smartcard session' do context 'user without a smartcard session' do
it "does not allow access" do it "does not allow access" do
subject subject
......
...@@ -54,26 +54,30 @@ RSpec.describe Repositories::GitHttpController, type: :request do ...@@ -54,26 +54,30 @@ RSpec.describe Repositories::GitHttpController, type: :request do
project.add_developer(user) project.add_developer(user)
end end
context 'user with a smartcard session', :clean_gitlab_redis_shared_state do RSpec.shared_examples_for 'smartcard session' do
let(:session_id) { '42' } context 'user with a smartcard session' do
let(:stored_session) do let(:session_id) { '42' }
{ 'smartcard_signins' => { 'last_signin_at' => 5.minutes.ago } } let(:stored_session) do
end { 'smartcard_signins' => { 'last_signin_at' => 5.minutes.ago } }
end
before do before do
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session)) redis.set("session:gitlab:#{session_id}", Marshal.dump(stored_session))
redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id]) redis.sadd("session:lookup:user:gitlab:#{user.id}", [session_id])
end
end end
end
it "allows access" do it "allows access" do
subject subject
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
end
end end
end end
it_behaves_like 'redis sessions store', 'smartcard session'
context 'user without a smartcard session' do context 'user without a smartcard session' do
it "does not allow access" do it "does not allow access" do
subject subject
......
...@@ -2,31 +2,43 @@ ...@@ -2,31 +2,43 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe ApplicationCable::Connection, :clean_gitlab_redis_shared_state do RSpec.describe ApplicationCable::Connection do
let(:session_id) { Rack::Session::SessionId.new('6919a6f1bb119dd7396fadc38fd18d0d') } RSpec.shared_examples_for 'ApplicationCable::Connection' do
let(:session_id) { Rack::Session::SessionId.new('6919a6f1bb119dd7396fadc38fd18d0d') }
context 'when session cookie is set' do
before do
redis_store_class.with do |redis|
redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash))
end
context 'when session cookie is set' do cookies[Gitlab::Application.config.session_options[:key]] = session_id.public_id
before do
Gitlab::Redis::SharedState.with do |redis|
redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash))
end end
cookies[Gitlab::Application.config.session_options[:key]] = session_id.public_id context 'when user is logged in' do
end let(:user) { create(:user) }
let(:session_hash) { { 'warden.user.user.key' => [[user.id], user.encrypted_password[0, 29]] } }
context 'when user is logged in' do it 'sets current_user' do
let(:user) { create(:user) } connect
let(:session_hash) { { 'warden.user.user.key' => [[user.id], user.encrypted_password[0, 29]] } }
it 'sets current_user' do expect(connection.current_user).to eq(user)
connect end
expect(connection.current_user).to eq(user) context 'with a stale password' do
let(:partial_password_hash) { build(:user, password: 'some_old_password').encrypted_password[0, 29] }
let(:session_hash) { { 'warden.user.user.key' => [[user.id], partial_password_hash] } }
it 'sets current_user to nil' do
connect
expect(connection.current_user).to be_nil
end
end
end end
context 'with a stale password' do context 'when user is not logged in' do
let(:partial_password_hash) { build(:user, password: 'some_old_password').encrypted_password[0, 29] } let(:session_hash) { {} }
let(:session_hash) { { 'warden.user.user.key' => [[user.id], partial_password_hash] } }
it 'sets current_user to nil' do it 'sets current_user to nil' do
connect connect
...@@ -36,32 +48,24 @@ RSpec.describe ApplicationCable::Connection, :clean_gitlab_redis_shared_state do ...@@ -36,32 +48,24 @@ RSpec.describe ApplicationCable::Connection, :clean_gitlab_redis_shared_state do
end end
end end
context 'when user is not logged in' do context 'when session cookie is not set' do
let(:session_hash) { {} }
it 'sets current_user to nil' do it 'sets current_user to nil' do
connect connect
expect(connection.current_user).to be_nil expect(connection.current_user).to be_nil
end end
end end
end
context 'when session cookie is not set' do context 'when session cookie is an empty string' do
it 'sets current_user to nil' do it 'sets current_user to nil' do
connect cookies[Gitlab::Application.config.session_options[:key]] = ''
expect(connection.current_user).to be_nil
end
end
context 'when session cookie is an empty string' do
it 'sets current_user to nil' do
cookies[Gitlab::Application.config.session_options[:key]] = ''
connect connect
expect(connection.current_user).to be_nil expect(connection.current_user).to be_nil
end
end end
end end
it_behaves_like 'redis sessions store', 'ApplicationCable::Connection'
end end
...@@ -2,70 +2,74 @@ ...@@ -2,70 +2,74 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Active user sessions', :clean_gitlab_redis_shared_state do RSpec.describe 'Active user sessions' do
it 'successful login adds a new active user login' do RSpec.shared_examples_for 'active user sessions' do
now = Time.zone.parse('2018-03-12 09:06') it 'successful login adds a new active user login' do
Timecop.freeze(now) do now = Time.zone.parse('2018-03-12 09:06')
user = create(:user) Timecop.freeze(now) do
gitlab_sign_in(user) user = create(:user)
expect(current_path).to eq root_path gitlab_sign_in(user)
expect(current_path).to eq root_path
sessions = ActiveSession.list(user)
expect(sessions.count).to eq 1
# refresh the current page updates the updated_at
Timecop.freeze(now + 1.minute) do
visit current_path
sessions = ActiveSession.list(user) sessions = ActiveSession.list(user)
expect(sessions.first).to have_attributes( expect(sessions.count).to eq 1
created_at: Time.zone.parse('2018-03-12 09:06'),
updated_at: Time.zone.parse('2018-03-12 09:07') # refresh the current page updates the updated_at
) Timecop.freeze(now + 1.minute) do
visit current_path
sessions = ActiveSession.list(user)
expect(sessions.first).to have_attributes(
created_at: Time.zone.parse('2018-03-12 09:06'),
updated_at: Time.zone.parse('2018-03-12 09:07')
)
end
end end
end end
end
it 'successful login cleans up obsolete entries' do it 'successful login cleans up obsolete entries' do
user = create(:user) user = create(:user)
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
redis.sadd("session:lookup:user:gitlab:#{user.id}", '59822c7d9fcdfa03725eff41782ad97d') redis.sadd("session:lookup:user:gitlab:#{user.id}", '59822c7d9fcdfa03725eff41782ad97d')
end end
gitlab_sign_in(user) gitlab_sign_in(user)
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).not_to include '59822c7d9fcdfa03725eff41782ad97d' expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).not_to include '59822c7d9fcdfa03725eff41782ad97d'
end
end end
end
it 'sessionless login does not clean up obsolete entries' do it 'sessionless login does not clean up obsolete entries' do
user = create(:user) user = create(:user)
personal_access_token = create(:personal_access_token, user: user) personal_access_token = create(:personal_access_token, user: user)
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
redis.sadd("session:lookup:user:gitlab:#{user.id}", '59822c7d9fcdfa03725eff41782ad97d') redis.sadd("session:lookup:user:gitlab:#{user.id}", '59822c7d9fcdfa03725eff41782ad97d')
end end
visit user_path(user, :atom, private_token: personal_access_token.token) visit user_path(user, :atom, private_token: personal_access_token.token)
expect(page.status_code).to eq 200 expect(page.status_code).to eq 200
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).to include '59822c7d9fcdfa03725eff41782ad97d' expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).to include '59822c7d9fcdfa03725eff41782ad97d'
end
end end
end
it 'logout deletes the active user login' do it 'logout deletes the active user login' do
user = create(:user) user = create(:user)
gitlab_sign_in(user) gitlab_sign_in(user)
expect(current_path).to eq root_path expect(current_path).to eq root_path
expect(ActiveSession.list(user).count).to eq 1 expect(ActiveSession.list(user).count).to eq 1
gitlab_sign_out gitlab_sign_out
expect(current_path).to eq new_user_session_path expect(current_path).to eq new_user_session_path
expect(ActiveSession.list(user)).to be_empty expect(ActiveSession.list(user)).to be_empty
end
end end
it_behaves_like 'redis sessions store', 'active user sessions'
end end
...@@ -2,38 +2,42 @@ ...@@ -2,38 +2,42 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Session TTLs', :clean_gitlab_redis_shared_state do RSpec.describe 'Session TTLs' do
include SessionHelpers include SessionHelpers
it 'creates a session with a short TTL when login fails' do RSpec.shared_examples_for 'session ttls' do
visit new_user_session_path it 'creates a session with a short TTL when login fails' do
# The session key only gets created after a post visit new_user_session_path
fill_in 'user_login', with: 'non-existant@gitlab.org' # The session key only gets created after a post
fill_in 'user_password', with: '12345678' fill_in 'user_login', with: 'non-existant@gitlab.org'
click_button 'Sign in' fill_in 'user_password', with: '12345678'
click_button 'Sign in'
expect(page).to have_content('Invalid login or password') expect(page).to have_content('Invalid login or password')
expect_single_session_with_short_ttl expect_single_session_with_short_ttl(redis_store_class)
end end
it 'increases the TTL when the login succeeds' do it 'increases the TTL when the login succeeds' do
user = create(:user) user = create(:user)
gitlab_sign_in(user) gitlab_sign_in(user)
expect(page).to have_content(user.name) expect(page).to have_content(user.name)
expect_single_session_with_authenticated_ttl expect_single_session_with_authenticated_ttl(redis_store_class)
end end
context 'with an unauthorized project' do context 'with an unauthorized project' do
let_it_be(:project) { create(:project, :repository) } let_it_be(:project) { create(:project, :repository) }
it 'creates a session with a short TTL' do it 'creates a session with a short TTL' do
visit project_raw_path(project, 'master/README.md') visit project_raw_path(project, 'master/README.md')
expect_single_session_with_short_ttl expect_single_session_with_short_ttl(redis_store_class)
expect(page).to have_current_path(new_user_session_path) expect(page).to have_current_path(new_user_session_path)
end
end end
end end
it_behaves_like 'redis sessions store', 'session ttls'
end end
This diff is collapsed.
...@@ -10,25 +10,37 @@ RSpec.describe 'Session initializer for GitLab' do ...@@ -10,25 +10,37 @@ RSpec.describe 'Session initializer for GitLab' do
end end
describe 'config#session_store' do describe 'config#session_store' do
context 'when the GITLAB_REDIS_STORE_WITH_SESSION_STORE env is not set' do context 'when the GITLAB_USE_REDIS_SESSIONS_STORE env is not set' do
before do before do
stub_env('GITLAB_REDIS_STORE_WITH_SESSION_STORE', nil) stub_env('GITLAB_USE_REDIS_SESSIONS_STORE', nil)
end end
it 'initialized as a redis_store with a proper Redis::Store instance' do it 'initialized with Multistore as ENV var defaults to true' do
expect(subject).to receive(:session_store).with(:redis_store, a_hash_including(redis_store: kind_of(::Redis::Store))) expect(subject).to receive(:session_store).with(:redis_store, a_hash_including(redis_store: kind_of(::Redis::Store)))
load_session_store load_session_store
end end
end end
context 'when the GITLAB_REDIS_STORE_WITH_SESSION_STORE env is disabled' do context 'when the GITLAB_USE_REDIS_SESSIONS_STORE env is disabled' do
before do before do
stub_env('GITLAB_REDIS_STORE_WITH_SESSION_STORE', false) stub_env('GITLAB_USE_REDIS_SESSIONS_STORE', false)
end end
it 'initialized as a redis_store with a proper servers configuration' do it 'initialized as a redis_store with a proper servers configuration' do
expect(subject).to receive(:session_store).with(:redis_store, a_hash_including(servers: kind_of(Hash))) expect(subject).to receive(:session_store).with(:redis_store, a_hash_including(redis_store: kind_of(Redis::Store)))
load_session_store
end
end
context 'when the GITLAB_USE_REDIS_SESSIONS_STORE env is enabled' do
before do
stub_env('GITLAB_USE_REDIS_SESSIONS_STORE', true)
end
it 'initialized as a redis_store with a proper servers configuration' do
expect(subject).to receive(:session_store).with(:redis_store, a_hash_including(redis_store: kind_of(::Redis::Store)))
load_session_store load_session_store
end end
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::AnonymousSession, :clean_gitlab_redis_shared_state do RSpec.describe Gitlab::AnonymousSession do
let(:default_session_id) { '6919a6f1bb119dd7396fadc38fd18d0d' } let(:default_session_id) { '6919a6f1bb119dd7396fadc38fd18d0d' }
let(:additional_session_id) { '7919a6f1bb119dd7396fadc38fd18d0d' } let(:additional_session_id) { '7919a6f1bb119dd7396fadc38fd18d0d' }
...@@ -12,56 +12,60 @@ RSpec.describe Gitlab::AnonymousSession, :clean_gitlab_redis_shared_state do ...@@ -12,56 +12,60 @@ RSpec.describe Gitlab::AnonymousSession, :clean_gitlab_redis_shared_state do
described_class.new('127.0.0.1') described_class.new('127.0.0.1')
end end
describe '#store_session_ip' do RSpec.shared_examples_for 'anonymous sessions' do
it 'adds session id to proper key' do describe '#store_session_ip' do
subject.count_session_ip it 'adds session id to proper key' do
subject.count_session_ip
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
expect(redis.get("session:lookup:ip:gitlab2:127.0.0.1").to_i).to eq 1 expect(redis.get("session:lookup:ip:gitlab2:127.0.0.1").to_i).to eq 1
end
end end
end
it 'adds expiration time to key' do it 'adds expiration time to key' do
freeze_time do freeze_time do
subject.count_session_ip subject.count_session_ip
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
expect(redis.ttl("session:lookup:ip:gitlab2:127.0.0.1")).to eq(24.hours.to_i) expect(redis.ttl("session:lookup:ip:gitlab2:127.0.0.1")).to eq(24.hours.to_i)
end
end end
end end
end
context 'when there is already one session' do context 'when there is already one session' do
it 'increments the session count' do it 'increments the session count' do
subject.count_session_ip subject.count_session_ip
new_anonymous_session.count_session_ip new_anonymous_session.count_session_ip
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
expect(redis.get("session:lookup:ip:gitlab2:127.0.0.1").to_i).to eq(2) expect(redis.get("session:lookup:ip:gitlab2:127.0.0.1").to_i).to eq(2)
end
end end
end end
end end
end
describe '#stored_sessions' do describe '#stored_sessions' do
it 'returns all anonymous sessions per ip' do it 'returns all anonymous sessions per ip' do
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
redis.set("session:lookup:ip:gitlab2:127.0.0.1", 2) redis.set("session:lookup:ip:gitlab2:127.0.0.1", 2)
end end
expect(subject.session_count).to eq(2) expect(subject.session_count).to eq(2)
end
end end
end
it 'removes obsolete lookup through ip entries' do it 'removes obsolete lookup through ip entries' do
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
redis.set("session:lookup:ip:gitlab2:127.0.0.1", 2) redis.set("session:lookup:ip:gitlab2:127.0.0.1", 2)
end end
subject.cleanup_session_per_ip_count subject.cleanup_session_per_ip_count
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
expect(redis.exists("session:lookup:ip:gitlab2:127.0.0.1")).to eq(false) expect(redis.exists("session:lookup:ip:gitlab2:127.0.0.1")).to eq(false)
end
end end
end end
it_behaves_like 'redis sessions store', 'anonymous sessions'
end end
...@@ -4,4 +4,54 @@ require 'spec_helper' ...@@ -4,4 +4,54 @@ require 'spec_helper'
RSpec.describe Gitlab::Redis::Sessions do RSpec.describe Gitlab::Redis::Sessions do
include_examples "redis_new_instance_shared_examples", 'sessions', Gitlab::Redis::SharedState include_examples "redis_new_instance_shared_examples", 'sessions', Gitlab::Redis::SharedState
describe 'redis instance used in connection pool' do
before do
clear_pool
end
context 'when redis.sessions configuration is not provided' do
it 'uses ::Redis instance' do
expect(described_class).to receive(:config_fallback?).and_return(true)
described_class.pool.with do |redis_instance|
expect(redis_instance).to be_instance_of(::Redis)
end
end
end
context 'when redis.sessions configuration is provided' do
it 'instantiates an instance of MultiStore' do
expect(described_class).to receive(:config_fallback?).and_return(false)
described_class.pool.with do |redis_instance|
expect(redis_instance).to be_instance_of(::Gitlab::Redis::MultiStore)
end
end
end
def clear_pool
described_class.remove_instance_variable(:@pool)
rescue NameError
# raised if @pool was not set; ignore
end
end
describe '#store' do
subject { described_class.store(namespace: described_class::SESSION_NAMESPACE) }
context 'when redis.sessions configuration is provided' do
it 'instantiates ::Redis instance' do
expect(described_class).to receive(:config_fallback?).and_return(true)
expect(subject).to be_instance_of(::Redis::Store)
end
end
context 'when redis.sessions configuration is not provided' do
it 'instantiates an instance of MultiStore' do
expect(described_class).to receive(:config_fallback?).and_return(false)
expect(subject).to be_instance_of(::Gitlab::Redis::MultiStore)
end
end
end
end end
This diff is collapsed.
...@@ -376,24 +376,28 @@ RSpec.describe API::Commits do ...@@ -376,24 +376,28 @@ RSpec.describe API::Commits do
end end
end end
context 'when using warden' do RSpec.shared_examples_for 'warden user session' do
it 'increments usage counters', :clean_gitlab_redis_shared_state do context 'when using warden' do
session_id = Rack::Session::SessionId.new('6919a6f1bb119dd7396fadc38fd18d0d') it 'increments usage counters' do
session_hash = { 'warden.user.user.key' => [[user.id], user.encrypted_password[0, 29]] } session_id = Rack::Session::SessionId.new('6919a6f1bb119dd7396fadc38fd18d0d')
session_hash = { 'warden.user.user.key' => [[user.id], user.encrypted_password[0, 29]] }
Gitlab::Redis::SharedState.with do |redis|
redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash)) redis_store_class.with do |redis|
end redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash))
end
cookies[Gitlab::Application.config.session_options[:key]] = session_id.public_id cookies[Gitlab::Application.config.session_options[:key]] = session_id.public_id
expect(::Gitlab::UsageDataCounters::WebIdeCounter).to receive(:increment_commits_count) expect(::Gitlab::UsageDataCounters::WebIdeCounter).to receive(:increment_commits_count)
expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).to receive(:track_web_ide_edit_action) expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).to receive(:track_web_ide_edit_action)
post api(url), params: valid_c_params post api(url), params: valid_c_params
end
end end
end end
it_behaves_like 'redis sessions store', 'warden user session'
context 'a new file in project repo' do context 'a new file in project repo' do
before do before do
post api(url, user), params: valid_c_params post api(url, user), params: valid_c_params
......
# frozen_string_literal: true # frozen_string_literal: true
module SessionHelpers module SessionHelpers
def expect_single_session_with_authenticated_ttl def expect_single_session_with_authenticated_ttl(redis_store_class)
expect_single_session_with_expiration(Settings.gitlab['session_expire_delay'] * 60) expect_single_session_with_expiration(redis_store_class, Settings.gitlab['session_expire_delay'] * 60)
end end
def expect_single_session_with_short_ttl def expect_single_session_with_short_ttl(redis_store_class)
expect_single_session_with_expiration(Settings.gitlab['unauthenticated_session_expire_delay']) expect_single_session_with_expiration(redis_store_class, Settings.gitlab['unauthenticated_session_expire_delay'])
end end
def expect_single_session_with_expiration(expiration) def expect_single_session_with_expiration(redis_store_class, expiration)
session_keys = get_session_keys session_keys = get_session_keys(redis_store_class)
expect(session_keys.size).to eq(1) expect(session_keys.size).to eq(1)
expect(get_ttl(session_keys.first)).to be_within(5).of(expiration) expect(get_ttl(redis_store_class, session_keys.first)).to be_within(5).of(expiration)
end end
def get_session_keys def get_session_keys(redis_store_class)
Gitlab::Redis::SharedState.with { |redis| redis.scan_each(match: 'session:gitlab:*').to_a } redis_store_class.with { |redis| redis.scan_each(match: 'session:gitlab:*').to_a }
end end
def get_ttl(key) def get_ttl(redis_store_class, key)
Gitlab::Redis::SharedState.with { |redis| redis.ttl(key) } redis_store_class.with { |redis| redis.ttl(key) }
end end
end end
...@@ -93,18 +93,23 @@ RSpec.shared_examples "redis_shared_examples" do ...@@ -93,18 +93,23 @@ RSpec.shared_examples "redis_shared_examples" do
subject { described_class.new(rails_env).store } subject { described_class.new(rails_env).store }
shared_examples 'redis store' do shared_examples 'redis store' do
let(:redis_store) { ::Redis::Store }
let(:redis_store_to_s) { "Redis Client connected to #{host} against DB #{redis_database}" }
it 'instantiates Redis::Store' do it 'instantiates Redis::Store' do
is_expected.to be_a(::Redis::Store) is_expected.to be_a(redis_store)
expect(subject.to_s).to eq("Redis Client connected to #{host} against DB #{redis_database}")
expect(subject.to_s).to eq(redis_store_to_s)
end end
context 'with the namespace' do context 'with the namespace' do
let(:namespace) { 'namespace_name' } let(:namespace) { 'namespace_name' }
let(:redis_store_to_s) { "Redis Client connected to #{host} against DB #{redis_database} with namespace #{namespace}" }
subject { described_class.new(rails_env).store(namespace: namespace) } subject { described_class.new(rails_env).store(namespace: namespace) }
it "uses specified namespace" do it "uses specified namespace" do
expect(subject.to_s).to eq("Redis Client connected to #{host} against DB #{redis_database} with namespace #{namespace}") expect(subject.to_s).to eq(redis_store_to_s)
end end
end end
end end
......
# frozen_string_literal: true
RSpec.shared_examples 'redis sessions store' do |example|
context 'when ENV[GITLAB_USE_REDIS_SESSIONS_STORE] is true', :clean_gitlab_redis_sessions do
before do
stub_env('GITLAB_USE_REDIS_SESSIONS_STORE', 'true')
end
it_behaves_like example do
let(:redis_store_class) { Gitlab::Redis::Sessions }
end
end
context 'when ENV[GITLAB_USE_REDIS_SESSIONS_STORE] is false', :clean_gitlab_redis_shared_state do
before do
stub_env('GITLAB_USE_REDIS_SESSIONS_STORE', 'false')
end
it_behaves_like example do
let(:redis_store_class) { Gitlab::Redis::SharedState }
end
end
end
...@@ -18,32 +18,36 @@ RSpec.shared_examples 'snippet edit usage data counters' do ...@@ -18,32 +18,36 @@ RSpec.shared_examples 'snippet edit usage data counters' do
end end
end end
context 'when user is not sessionless' do RSpec.shared_examples_for 'sessionless user' do
before do context 'when user is not sessionless' do
session_id = Rack::Session::SessionId.new('6919a6f1bb119dd7396fadc38fd18d0d') before do
session_hash = { 'warden.user.user.key' => [[current_user.id], current_user.encrypted_password[0, 29]] } session_id = Rack::Session::SessionId.new('6919a6f1bb119dd7396fadc38fd18d0d')
session_hash = { 'warden.user.user.key' => [[current_user.id], current_user.encrypted_password[0, 29]] }
Gitlab::Redis::SharedState.with do |redis| redis_store_class.with do |redis|
redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash)) redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash))
end end
cookies[Gitlab::Application.config.session_options[:key]] = session_id.public_id cookies[Gitlab::Application.config.session_options[:key]] = session_id.public_id
end end
it 'tracks usage data actions', :clean_gitlab_redis_shared_state do it 'tracks usage data actions' do
expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).to receive(:track_snippet_editor_edit_action) expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).to receive(:track_snippet_editor_edit_action)
post_graphql_mutation(mutation) post_graphql_mutation(mutation)
end end
context 'when mutation result raises an error' do context 'when mutation result raises an error' do
it 'does not track usage data actions' do it 'does not track usage data actions' do
mutation_vars[:title] = nil mutation_vars[:title] = nil
expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).not_to receive(:track_snippet_editor_edit_action) expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).not_to receive(:track_snippet_editor_edit_action)
post_graphql_mutation(mutation) post_graphql_mutation(mutation)
end
end end
end end
end end
it_behaves_like 'redis sessions store', 'sessionless user'
end end
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment