summaryrefslogtreecommitdiff
path: root/app/models/active_session.rb
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/active_session.rb')
-rw-r--r--app/models/active_session.rb50
1 files changed, 36 insertions, 14 deletions
diff --git a/app/models/active_session.rb b/app/models/active_session.rb
index 00192b1da59..08352caca07 100644
--- a/app/models/active_session.rb
+++ b/app/models/active_session.rb
@@ -4,6 +4,7 @@ class ActiveSession
include ActiveModel::Model
SESSION_BATCH_SIZE = 200
+ ALLOWED_NUMBER_OF_ACTIVE_SESSIONS = 100
attr_accessor :created_at, :updated_at,
:session_id, :ip_address,
@@ -65,21 +66,22 @@ class ActiveSession
def self.destroy(user, session_id)
Gitlab::Redis::SharedState.with do |redis|
- redis.srem(lookup_key_name(user.id), session_id)
+ destroy_sessions(redis, user, [session_id])
+ end
+ end
- deleted_keys = redis.del(key_name(user.id, session_id))
+ def self.destroy_sessions(redis, user, session_ids)
+ key_names = session_ids.map {|session_id| key_name(user.id, session_id) }
+ session_names = session_ids.map {|session_id| "#{Gitlab::Redis::SharedState::SESSION_NAMESPACE}:#{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
+ redis.srem(lookup_key_name(user.id), session_ids)
+ redis.del(key_names)
+ redis.del(session_names)
end
def self.cleanup(user)
Gitlab::Redis::SharedState.with do |redis|
+ clean_up_old_sessions(redis, user)
cleaned_up_lookup_entries(redis, user)
end
end
@@ -118,19 +120,39 @@ class ActiveSession
end
end
- def self.raw_active_session_entries(session_ids, user_id)
+ def self.raw_active_session_entries(redis, session_ids, user_id)
return [] if session_ids.empty?
- Gitlab::Redis::SharedState.with do |redis|
- entry_keys = session_ids.map { |session_id| key_name(user_id, session_id) }
+ entry_keys = session_ids.map { |session_id| key_name(user_id, session_id) }
+
+ redis.mget(entry_keys)
+ end
- redis.mget(entry_keys)
+ def self.active_session_entries(session_ids, user_id, redis)
+ return [] if session_ids.empty?
+
+ entry_keys = raw_active_session_entries(redis, session_ids, user_id)
+
+ entry_keys.map do |raw_session|
+ Marshal.load(raw_session) # rubocop:disable Security/MarshalLoad
end
end
+ def self.clean_up_old_sessions(redis, user)
+ session_ids = session_ids_for_user(user.id)
+
+ return if session_ids.count <= ALLOWED_NUMBER_OF_ACTIVE_SESSIONS
+
+ # remove sessions if there are more than ALLOWED_NUMBER_OF_ACTIVE_SESSIONS.
+ sessions = active_session_entries(session_ids, user.id, redis)
+ sessions.sort_by! {|session| session.updated_at }.reverse!
+ sessions = sessions[ALLOWED_NUMBER_OF_ACTIVE_SESSIONS..-1].map { |session| session.session_id }
+ destroy_sessions(redis, user, sessions)
+ end
+
def self.cleaned_up_lookup_entries(redis, user)
session_ids = session_ids_for_user(user.id)
- entries = raw_active_session_entries(session_ids, user.id)
+ entries = raw_active_session_entries(redis, session_ids, user.id)
# remove expired keys.
# only the single key entries are automatically expired by redis, the