diff options
author | Douwe Maan <douwe@gitlab.com> | 2018-05-02 14:56:47 +0300 |
---|---|---|
committer | Douwe Maan <douwe@gitlab.com> | 2018-05-02 14:56:47 +0300 |
commit | e3b15d37180b49949e1ea19210f3ea219b645ec1 (patch) | |
tree | 1e070cf5943034b85a8ddc74301681814301ed84 | |
parent | 75f6e91fcde12029068f48b6b7fb3fd13a612c61 (diff) |
Revert "Merge branch 'feature/display-active-sessions' into 'master'"revert-1350d80c
This reverts merge request !17867
23 files changed, 47 insertions, 642 deletions
@@ -184,9 +184,6 @@ gem 're2', '~> 1.1.1' gem 'version_sorter', '~> 2.1.0' -# User agent parsing -gem 'device_detector' - # Cache gem 'redis-rails', '~> 5.0.2' diff --git a/Gemfile.lock b/Gemfile.lock index f11df6a283e..9b2c47587ee 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -161,7 +161,6 @@ GEM activerecord (>= 3.2.0, < 5.1) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) - device_detector (1.0.0) devise (4.2.0) bcrypt (~> 3.0) orm_adapter (~> 0.1) @@ -1027,7 +1026,6 @@ DEPENDENCIES database_cleaner (~> 1.5.0) deckar01-task_list (= 2.0.0) default_value_for (~> 3.0.0) - device_detector devise (~> 4.2) devise-two-factor (~> 3.0.0) diffy (~> 3.1.0) diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 2faea55a5f5..e058a0b35b7 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -452,7 +452,6 @@ img.emoji { /** COMMON CLASSES **/ .prepend-top-0 { margin-top: 0; } -.prepend-top-2 { margin-top: 2px; } .prepend-top-5 { margin-top: 5px; } .prepend-top-8 { margin-top: $grid-size; } .prepend-top-10 { margin-top: 10px; } diff --git a/app/assets/stylesheets/framework/images.scss b/app/assets/stylesheets/framework/images.scss index ab3cceceae9..62a0fba3da3 100644 --- a/app/assets/stylesheets/framework/images.scss +++ b/app/assets/stylesheets/framework/images.scss @@ -39,10 +39,35 @@ svg { fill: currentColor; - $svg-sizes: 8 12 16 18 24 32 48 72; - @each $svg-size in $svg-sizes { - &.s#{$svg-size} { - @include svg-size(#{$svg-size}px); - } + &.s8 { + @include svg-size(8px); + } + + &.s12 { + @include svg-size(12px); + } + + &.s16 { + @include svg-size(16px); + } + + &.s18 { + @include svg-size(18px); + } + + &.s24 { + @include svg-size(24px); + } + + &.s32 { + @include svg-size(32px); + } + + &.s48 { + @include svg-size(48px); + } + + &.s72 { + @include svg-size(72px); } } diff --git a/app/controllers/profiles/active_sessions_controller.rb b/app/controllers/profiles/active_sessions_controller.rb deleted file mode 100644 index f0cdc228366..00000000000 --- a/app/controllers/profiles/active_sessions_controller.rb +++ /dev/null @@ -1,14 +0,0 @@ -class Profiles::ActiveSessionsController < Profiles::ApplicationController - def index - @sessions = ActiveSession.list(current_user) - end - - def destroy - ActiveSession.destroy(current_user, params[:id]) - - respond_to do |format| - format.html { redirect_to profile_active_sessions_url, status: 302 } - format.js { head :ok } - end - end -end diff --git a/app/helpers/active_sessions_helper.rb b/app/helpers/active_sessions_helper.rb deleted file mode 100644 index 97b6dac67c5..00000000000 --- a/app/helpers/active_sessions_helper.rb +++ /dev/null @@ -1,23 +0,0 @@ -module ActiveSessionsHelper - # Maps a device type as defined in `ActiveSession` to an svg icon name and - # outputs the icon html. - # - # see `DeviceDetector::Device::DEVICE_NAMES` about the available device types - def active_session_device_type_icon(active_session) - icon_name = - case active_session.device_type - when 'smartphone', 'feature phone', 'phablet' - 'mobile' - when 'tablet' - 'tablet' - when 'tv', 'smart display', 'camera', 'portable media player', 'console' - 'media' - when 'car browser' - 'car' - else - 'monitor-o' - end - - sprite_icon(icon_name, size: 16, css_class: 'prepend-top-2') - end -end diff --git a/app/models/active_session.rb b/app/models/active_session.rb deleted file mode 100644 index b4a86dbb331..00000000000 --- a/app/models/active_session.rb +++ /dev/null @@ -1,110 +0,0 @@ -class ActiveSession - include ActiveModel::Model - - attr_accessor :created_at, :updated_at, - :session_id, :ip_address, - :browser, :os, :device_name, :device_type - - def current?(session) - return false if session_id.nil? || session.id.nil? - - session_id == session.id - end - - def human_device_type - device_type&.titleize - end - - def self.set(user, request) - Gitlab::Redis::SharedState.with do |redis| - session_id = request.session.id - client = DeviceDetector.new(request.user_agent) - timestamp = Time.current - - active_user_session = new( - ip_address: request.ip, - browser: client.name, - os: client.os_name, - device_name: client.device_name, - device_type: client.device_type, - created_at: user.current_sign_in_at || timestamp, - updated_at: timestamp, - session_id: session_id - ) - - redis.pipelined do - redis.setex( - key_name(user.id, session_id), - Settings.gitlab['session_expire_delay'] * 60, - Marshal.dump(active_user_session) - ) - - redis.sadd( - lookup_key_name(user.id), - session_id - ) - end - end - end - - def self.list(user) - Gitlab::Redis::SharedState.with do |redis| - cleaned_up_lookup_entries(redis, user.id).map do |entry| - # rubocop:disable Security/MarshalLoad - Marshal.load(entry) - # rubocop:enable Security/MarshalLoad - end - end - end - - def self.destroy(user, session_id) - Gitlab::Redis::SharedState.with do |redis| - redis.srem(lookup_key_name(user.id), session_id) - - deleted_keys = redis.del(key_name(user.id, session_id)) - - # only allow deleting the devise session if we could actually find a - # related active session. this prevents another user from deleting - # someone else's session. - if deleted_keys > 0 - redis.del("#{Gitlab::Redis::SharedState::SESSION_NAMESPACE}:#{session_id}") - end - end - end - - def self.cleanup(user) - Gitlab::Redis::SharedState.with do |redis| - cleaned_up_lookup_entries(redis, user.id) - end - end - - def self.key_name(user_id, session_id = '*') - "#{Gitlab::Redis::SharedState::USER_SESSIONS_NAMESPACE}:#{user_id}:#{session_id}" - end - - def self.lookup_key_name(user_id) - "#{Gitlab::Redis::SharedState::USER_SESSIONS_LOOKUP_NAMESPACE}:#{user_id}" - end - - def self.cleaned_up_lookup_entries(redis, user_id) - lookup_key = lookup_key_name(user_id) - - session_ids = redis.smembers(lookup_key) - - entry_keys = session_ids.map { |session_id| key_name(user_id, session_id) } - return [] if entry_keys.empty? - - entries = redis.mget(entry_keys) - - session_ids_and_entries = session_ids.zip(entries) - - # remove expired keys. - # only the single key entries are automatically expired by redis, the - # lookup entries in the set need to be removed manually. - session_ids_and_entries.reject { |_session_id, entry| entry }.each do |session_id, _entry| - redis.srem(lookup_key, session_id) - end - - session_ids_and_entries.select { |_session_id, entry| entry }.map { |_session_id, entry| entry } - end -end diff --git a/app/views/layouts/nav/sidebar/_profile.html.haml b/app/views/layouts/nav/sidebar/_profile.html.haml index 6cbd163dd41..c878fcf2808 100644 --- a/app/views/layouts/nav/sidebar/_profile.html.haml +++ b/app/views/layouts/nav/sidebar/_profile.html.haml @@ -129,17 +129,6 @@ = link_to profile_preferences_path do %strong.fly-out-top-item-name #{ _('Preferences') } - = nav_link(controller: :active_sessions) do - = link_to profile_active_sessions_path do - .nav-icon-container - = sprite_icon('monitor-lines') - %span.nav-item-name - Active Sessions - %ul.sidebar-sub-level-items.is-fly-out-only - = nav_link(controller: :active_sessions, html_options: { class: "fly-out-top-item" } ) do - = link_to profile_active_sessions_path do - %strong.fly-out-top-item-name - #{ _('Active Sessions') } = nav_link(path: 'profiles#audit_log') do = link_to audit_log_profile_path do .nav-icon-container diff --git a/app/views/profiles/active_sessions/_active_session.html.haml b/app/views/profiles/active_sessions/_active_session.html.haml deleted file mode 100644 index d40b771f48b..00000000000 --- a/app/views/profiles/active_sessions/_active_session.html.haml +++ /dev/null @@ -1,31 +0,0 @@ -- is_current_session = active_session.current?(session) - -%li - .pull-left.append-right-10{ data: { toggle: 'tooltip' }, title: active_session.human_device_type } - = active_session_device_type_icon(active_session) - - .description.pull-left - %div - %strong= active_session.ip_address - - if is_current_session - %div This is your current session - - else - %div - Last accessed on - = l(active_session.updated_at, format: :short) - - %div - %strong= active_session.browser - on - %strong= active_session.os - - %div - %strong Signed in - on - = l(active_session.created_at, format: :short) - - - unless is_current_session - .pull-right - = link_to profile_active_session_path(active_session.session_id), data: { confirm: 'Are you sure? The device will be signed out of GitLab.' }, method: :delete, class: "btn btn-danger prepend-left-10" do - %span.sr-only Revoke - Revoke diff --git a/app/views/profiles/active_sessions/index.html.haml b/app/views/profiles/active_sessions/index.html.haml deleted file mode 100644 index d0250bb4eab..00000000000 --- a/app/views/profiles/active_sessions/index.html.haml +++ /dev/null @@ -1,14 +0,0 @@ -- page_title 'Active Sessions' -- @content_class = "limit-container-width" unless fluid_layout - -.row.prepend-top-default - .col-lg-4.profile-settings-sidebar - %h4.prepend-top-0 - = page_title - %p - This is a list of devices that have logged into your account. Revoke any sessions that you do not recognize. - .col-lg-8 - .append-bottom-default - - %ul.well-list - = render partial: 'profiles/active_sessions/active_session', collection: @sessions diff --git a/changelogs/unreleased/feature-display-active-sessions.yml b/changelogs/unreleased/feature-display-active-sessions.yml deleted file mode 100644 index 14cfa66953e..00000000000 --- a/changelogs/unreleased/feature-display-active-sessions.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Display active sessions and allow the user to revoke any of it -merge_request: 17867 -author: Alexis Reigel -type: added diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index da24881885e..f2fde1e0048 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -15,15 +15,19 @@ cookie_key = if Rails.env.development? "_gitlab_session" end -sessions_config = Gitlab::Redis::SharedState.params -sessions_config[:namespace] = Gitlab::Redis::SharedState::SESSION_NAMESPACE +if Rails.env.test? + Gitlab::Application.config.session_store :cookie_store, key: "_gitlab_session" +else + sessions_config = Gitlab::Redis::SharedState.params + sessions_config[:namespace] = Gitlab::Redis::SharedState::SESSION_NAMESPACE -Gitlab::Application.config.session_store( - :redis_store, # Using the cookie_store would enable session replay attacks. - servers: sessions_config, - key: cookie_key, - secure: Gitlab.config.gitlab.https, - httponly: true, - expires_in: Settings.gitlab['session_expire_delay'] * 60, - path: Rails.application.config.relative_url_root.nil? ? '/' : Gitlab::Application.config.relative_url_root -) + Gitlab::Application.config.session_store( + :redis_store, # Using the cookie_store would enable session replay attacks. + servers: sessions_config, + key: cookie_key, + secure: Gitlab.config.gitlab.https, + httponly: true, + expires_in: Settings.gitlab['session_expire_delay'] * 60, + path: Rails.application.config.relative_url_root.nil? ? '/' : Gitlab::Application.config.relative_url_root + ) +end diff --git a/config/initializers/warden.rb b/config/initializers/warden.rb index bf079f8e1a7..ee034d21eae 100644 --- a/config/initializers/warden.rb +++ b/config/initializers/warden.rb @@ -6,16 +6,4 @@ Rails.application.configure do |config| Warden::Manager.before_failure do |env, opts| Gitlab::Auth::BlockedUserTracker.log_if_user_blocked(env) end - - Warden::Manager.after_authentication do |user, auth, opts| - ActiveSession.cleanup(user) - end - - Warden::Manager.after_set_user only: :fetch do |user, auth, opts| - ActiveSession.set(user, auth.request) - end - - Warden::Manager.before_logout do |user, auth, opts| - ActiveSession.destroy(user || auth.user, auth.request.session.id) - end end diff --git a/config/routes/profile.rb b/config/routes/profile.rb index a9ba5ac2c0b..bcfc17a5f66 100644 --- a/config/routes/profile.rb +++ b/config/routes/profile.rb @@ -30,7 +30,6 @@ resource :profile, only: [:show, :update] do put :revoke end end - resources :active_sessions, only: [:index, :destroy] resources :emails, only: [:index, :create, :destroy] do member do put :resend_confirmation_instructions diff --git a/doc/development/fe_guide/icons.md b/doc/development/fe_guide/icons.md index b469a9c6aef..b288ee95722 100644 --- a/doc/development/fe_guide/icons.md +++ b/doc/development/fe_guide/icons.md @@ -49,7 +49,7 @@ Please use the following function inside JS to render an icon : All Icons and Illustrations are managed in the [gitlab-svgs](https://gitlab.com/gitlab-org/gitlab-svgs) repository which is added as a dev-dependency. -To upgrade to a new SVG Sprite version run `yarn upgrade @gitlab-org/gitlab-svgs`. +To upgrade to a new SVG Sprite version run `yarn upgrade @gitlab-org/gitlab-svgs` and then run `yarn run svg`. This task will copy the svg sprite and all illustrations in the correct folders. The updated files should be tracked in Git as those are referenced. # SVG Illustrations diff --git a/doc/user/profile/active_sessions.md b/doc/user/profile/active_sessions.md deleted file mode 100644 index 5119c0e30d0..00000000000 --- a/doc/user/profile/active_sessions.md +++ /dev/null @@ -1,20 +0,0 @@ -# Active Sessions - -> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17867) -> in GitLab 10.8. - -GitLab lists all devices that have logged into your account. This allows you to -review the sessions and revoke any of it that you don't recognize. - -## Listing all active sessions - -1. On the upper right corner, click on your avatar and go to your **Settings**. -1. Navigate to the **Active Sessions** tab. - -![Active sessions list](img/active_sessions_list.png) - -## Revoking a session - -1. Navigate to your [profile's](#profile-settings) **Settings > Active Sessions**. -1. Click on **Revoke** besides a session. The current session cannot be - revoked, as this would sign you out of GitLab. diff --git a/doc/user/profile/img/active_sessions_list.png b/doc/user/profile/img/active_sessions_list.png Binary files differdeleted file mode 100644 index 76a52220bcd..00000000000 --- a/doc/user/profile/img/active_sessions_list.png +++ /dev/null diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md index 91cdef8d1dd..ab16f8d14c1 100644 --- a/doc/user/profile/index.md +++ b/doc/user/profile/index.md @@ -39,7 +39,6 @@ From there, you can: - Manage [SSH keys](../../ssh/README.md#ssh) to access your account via SSH - Manage your [preferences](preferences.md#syntax-highlighting-theme) to customize your own GitLab experience -- [View your active sessions](active_sessions.md) and revoke any of them if necessary - Access your audit log, a security log of important events involving your account ## Changing your username diff --git a/lib/gitlab/redis/shared_state.rb b/lib/gitlab/redis/shared_state.rb index e5a0fdae7ef..10bec7a90da 100644 --- a/lib/gitlab/redis/shared_state.rb +++ b/lib/gitlab/redis/shared_state.rb @@ -5,8 +5,6 @@ module Gitlab module Redis class SharedState < ::Gitlab::Redis::Wrapper SESSION_NAMESPACE = 'session:gitlab'.freeze - USER_SESSIONS_NAMESPACE = 'session:user:gitlab'.freeze - USER_SESSIONS_LOOKUP_NAMESPACE = 'session:lookup:user:gitlab'.freeze DEFAULT_REDIS_SHARED_STATE_URL = 'redis://localhost:6382'.freeze REDIS_SHARED_STATE_CONFIG_ENV_VAR_NAME = 'GITLAB_REDIS_SHARED_STATE_CONFIG_FILE'.freeze diff --git a/spec/controllers/projects/clusters/gcp_controller_spec.rb b/spec/controllers/projects/clusters/gcp_controller_spec.rb index 715bb9f5e52..e14ba29fa70 100644 --- a/spec/controllers/projects/clusters/gcp_controller_spec.rb +++ b/spec/controllers/projects/clusters/gcp_controller_spec.rb @@ -142,7 +142,7 @@ describe Projects::Clusters::GcpController do context 'when google project billing is enabled' do before do - redis_double = double.as_null_object + redis_double = double allow(Gitlab::Redis::SharedState).to receive(:with).and_yield(redis_double) allow(redis_double).to receive(:get).with(CheckGcpProjectBillingWorker.redis_shared_state_key_for('token')).and_return('true') end diff --git a/spec/features/profiles/active_sessions_spec.rb b/spec/features/profiles/active_sessions_spec.rb deleted file mode 100644 index 4045cfd21c4..00000000000 --- a/spec/features/profiles/active_sessions_spec.rb +++ /dev/null @@ -1,89 +0,0 @@ -require 'rails_helper' - -feature 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do - let(:user) do - create(:user).tap do |user| - user.current_sign_in_at = Time.current - end - end - - around do |example| - Timecop.freeze(Time.zone.parse('2018-03-12 09:06')) do - example.run - end - end - - scenario 'User sees their active sessions' do - Capybara::Session.new(:session1) - Capybara::Session.new(:session2) - - # note: headers can only be set on the non-js (aka. rack-test) driver - using_session :session1 do - Capybara.page.driver.header( - 'User-Agent', - 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:58.0) Gecko/20100101 Firefox/58.0' - ) - - gitlab_sign_in(user) - end - - # set an additional session on another device - using_session :session2 do - Capybara.page.driver.header( - 'User-Agent', - 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_1_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12B466 [FBDV/iPhone7,2]' - ) - - gitlab_sign_in(user) - end - - using_session :session1 do - visit profile_active_sessions_path - - expect(page).to have_content( - '127.0.0.1 ' \ - 'This is your current session ' \ - 'Firefox on Ubuntu ' \ - 'Signed in on 12 Mar 09:06' - ) - - expect(page).to have_selector '[title="Desktop"]', count: 1 - - expect(page).to have_content( - '127.0.0.1 ' \ - 'Last accessed on 12 Mar 09:06 ' \ - 'Mobile Safari on iOS ' \ - 'Signed in on 12 Mar 09:06' - ) - - expect(page).to have_selector '[title="Smartphone"]', count: 1 - end - end - - scenario 'User can revoke a session', :js, :redis_session_store do - Capybara::Session.new(:session1) - Capybara::Session.new(:session2) - - # set an additional session in another browser - using_session :session2 do - gitlab_sign_in(user) - end - - using_session :session1 do - gitlab_sign_in(user) - visit profile_active_sessions_path - - expect(page).to have_link('Revoke', count: 1) - - accept_confirm { click_on 'Revoke' } - - expect(page).not_to have_link('Revoke') - end - - using_session :session2 do - visit profile_active_sessions_path - - expect(page).to have_content('You need to sign in or sign up before continuing.') - end - end -end diff --git a/spec/features/users/active_sessions_spec.rb b/spec/features/users/active_sessions_spec.rb deleted file mode 100644 index 631d7e3bced..00000000000 --- a/spec/features/users/active_sessions_spec.rb +++ /dev/null @@ -1,69 +0,0 @@ -require 'spec_helper' - -feature 'Active user sessions', :clean_gitlab_redis_shared_state do - scenario 'Successful login adds a new active user login' do - now = Time.zone.parse('2018-03-12 09:06') - Timecop.freeze(now) do - user = create(:user) - 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) - 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 - - scenario 'Successful login cleans up obsolete entries' do - user = create(:user) - - Gitlab::Redis::SharedState.with do |redis| - redis.sadd("session:lookup:user:gitlab:#{user.id}", '59822c7d9fcdfa03725eff41782ad97d') - end - - gitlab_sign_in(user) - - Gitlab::Redis::SharedState.with do |redis| - expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).not_to include '59822c7d9fcdfa03725eff41782ad97d' - end - end - - scenario 'Sessionless login does not clean up obsolete entries' do - user = create(:user) - personal_access_token = create(:personal_access_token, user: user) - - Gitlab::Redis::SharedState.with do |redis| - redis.sadd("session:lookup:user:gitlab:#{user.id}", '59822c7d9fcdfa03725eff41782ad97d') - end - - visit user_path(user, :atom, private_token: personal_access_token.token) - expect(page.status_code).to eq 200 - - Gitlab::Redis::SharedState.with do |redis| - expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).to include '59822c7d9fcdfa03725eff41782ad97d' - end - end - - scenario 'Logout deletes the active user login' do - user = create(:user) - gitlab_sign_in(user) - expect(current_path).to eq root_path - - expect(ActiveSession.list(user).count).to eq 1 - - gitlab_sign_out - expect(current_path).to eq new_user_session_path - - expect(ActiveSession.list(user)).to be_empty - end -end diff --git a/spec/models/active_session_spec.rb b/spec/models/active_session_spec.rb deleted file mode 100644 index 129b2f92683..00000000000 --- a/spec/models/active_session_spec.rb +++ /dev/null @@ -1,216 +0,0 @@ -require 'rails_helper' - -RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do - let(:user) do - create(:user).tap do |user| - user.current_sign_in_at = Time.current - end - end - - let(:session) { double(:session, id: '6919a6f1bb119dd7396fadc38fd18d0d') } - - let(:request) do - double(:request, { - user_agent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_1_3 like Mac OS X) AppleWebKit/600.1.4 ' \ - '(KHTML, like Gecko) Mobile/12B466 [FBDV/iPhone7,2]', - ip: '127.0.0.1', - session: session - }) - end - - describe '#current?' do - it 'returns true if the active session matches the current session' do - active_session = ActiveSession.new(session_id: '6919a6f1bb119dd7396fadc38fd18d0d') - - expect(active_session.current?(session)).to be true - end - - it 'returns false if the active session does not match the current session' do - active_session = ActiveSession.new(session_id: '59822c7d9fcdfa03725eff41782ad97d') - - expect(active_session.current?(session)).to be false - end - - it 'returns false if the session id is nil' do - active_session = ActiveSession.new(session_id: nil) - session = double(:session, id: nil) - - expect(active_session.current?(session)).to be false - end - end - - describe '.list' do - it 'returns all sessions by user' do - Gitlab::Redis::SharedState.with do |redis| - redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", Marshal.dump({ session_id: 'a' })) - redis.set("session:user:gitlab:#{user.id}:59822c7d9fcdfa03725eff41782ad97d", Marshal.dump({ session_id: 'b' })) - redis.set("session:user:gitlab:9999:5c8611e4f9c69645ad1a1492f4131358", '') - - redis.sadd( - "session:lookup:user:gitlab:#{user.id}", - %w[ - 6919a6f1bb119dd7396fadc38fd18d0d - 59822c7d9fcdfa03725eff41782ad97d - ] - ) - end - - expect(ActiveSession.list(user)).to match_array [{ session_id: 'a' }, { session_id: 'b' }] - end - - it 'does not return obsolete entries and cleans them up' do - Gitlab::Redis::SharedState.with do |redis| - redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", Marshal.dump({ session_id: 'a' })) - - redis.sadd( - "session:lookup:user:gitlab:#{user.id}", - %w[ - 6919a6f1bb119dd7396fadc38fd18d0d - 59822c7d9fcdfa03725eff41782ad97d - ] - ) - end - - expect(ActiveSession.list(user)).to eq [{ session_id: 'a' }] - - Gitlab::Redis::SharedState.with do |redis| - expect(redis.sscan_each("session:lookup:user:gitlab:#{user.id}").to_a).to eq ['6919a6f1bb119dd7396fadc38fd18d0d'] - end - end - - it 'returns an empty array if the use does not have any active session' do - expect(ActiveSession.list(user)).to eq [] - end - end - - describe '.set' do - it 'sets a new redis entry for the user session and a lookup entry' do - ActiveSession.set(user, request) - - Gitlab::Redis::SharedState.with do |redis| - expect(redis.scan_each.to_a).to match_array [ - "session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", - "session:lookup:user:gitlab:#{user.id}" - ] - end - end - - it 'adds timestamps and information from the request' do - Timecop.freeze(Time.zone.parse('2018-03-12 09:06')) do - ActiveSession.set(user, request) - - session = ActiveSession.list(user) - - expect(session.count).to eq 1 - expect(session.first).to have_attributes( - ip_address: '127.0.0.1', - browser: 'Mobile Safari', - os: 'iOS', - device_name: 'iPhone 6', - device_type: 'smartphone', - created_at: Time.zone.parse('2018-03-12 09:06'), - updated_at: Time.zone.parse('2018-03-12 09:06'), - session_id: '6919a6f1bb119dd7396fadc38fd18d0d' - ) - end - end - - it 'keeps the created_at from the login on consecutive requests' do - now = Time.zone.parse('2018-03-12 09:06') - - Timecop.freeze(now) do - ActiveSession.set(user, request) - - Timecop.freeze(now + 1.minute) do - ActiveSession.set(user, request) - - session = ActiveSession.list(user) - - expect(session.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 - - describe '.destroy' do - it 'removes the entry associated with the currently killed user session' do - Gitlab::Redis::SharedState.with do |redis| - redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '') - redis.set("session:user:gitlab:#{user.id}:59822c7d9fcdfa03725eff41782ad97d", '') - redis.set("session:user:gitlab:9999:5c8611e4f9c69645ad1a1492f4131358", '') - end - - ActiveSession.destroy(user, request.session.id) - - Gitlab::Redis::SharedState.with do |redis| - expect(redis.scan_each(match: "session:user:gitlab:*")).to match_array [ - "session:user:gitlab:#{user.id}:59822c7d9fcdfa03725eff41782ad97d", - "session:user:gitlab:9999:5c8611e4f9c69645ad1a1492f4131358" - ] - end - end - - it 'removes the lookup entry' do - Gitlab::Redis::SharedState.with do |redis| - redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '') - redis.sadd("session:lookup:user:gitlab:#{user.id}", '6919a6f1bb119dd7396fadc38fd18d0d') - end - - ActiveSession.destroy(user, request.session.id) - - Gitlab::Redis::SharedState.with do |redis| - expect(redis.scan_each(match: "session:lookup:user:gitlab:#{user.id}").to_a).to be_empty - end - end - - it 'removes the devise session' do - Gitlab::Redis::SharedState.with do |redis| - redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '') - redis.set("session:gitlab:6919a6f1bb119dd7396fadc38fd18d0d", '') - end - - ActiveSession.destroy(user, request.session.id) - - Gitlab::Redis::SharedState.with do |redis| - expect(redis.scan_each(match: "session:gitlab:*").to_a).to be_empty - end - end - - it 'does not remove the devise session if the active session could not be found' do - Gitlab::Redis::SharedState.with do |redis| - redis.set("session:gitlab:6919a6f1bb119dd7396fadc38fd18d0d", '') - end - - other_user = create(:user) - - ActiveSession.destroy(other_user, request.session.id) - - Gitlab::Redis::SharedState.with do |redis| - expect(redis.scan_each(match: "session:gitlab:*").to_a).not_to be_empty - end - end - end - - describe '.cleanup' do - it 'removes obsolete lookup entries' do - Gitlab::Redis::SharedState.with do |redis| - redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '') - redis.sadd("session:lookup:user:gitlab:#{user.id}", '6919a6f1bb119dd7396fadc38fd18d0d') - redis.sadd("session:lookup:user:gitlab:#{user.id}", '59822c7d9fcdfa03725eff41782ad97d') - end - - ActiveSession.cleanup(user) - - Gitlab::Redis::SharedState.with do |redis| - expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).to eq ['6919a6f1bb119dd7396fadc38fd18d0d'] - end - end - - it 'does not bail if there are no lookup entries' do - ActiveSession.cleanup(user) - end - end -end |