From ef24fd54ba1d8ceba1915f04b1cd82047dcbc038 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 13 Mar 2017 10:19:02 -0400 Subject: [PATCH] FEATUE: automatically validate token is stored in redis This ensures we have some handling for redis flushall We attempt to recover our in-memory session token once every 30 seconds Code is careful to only set the token if it is nil, to allow for manual cycling to remain safe if needed --- app/models/global_setting.rb | 14 ++++++++++++++ spec/models/global_setting_spec.rb | 21 +++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/app/models/global_setting.rb b/app/models/global_setting.rb index bf570ed72d6..a3ad8e4bee2 100644 --- a/app/models/global_setting.rb +++ b/app/models/global_setting.rb @@ -11,6 +11,8 @@ class GlobalSetting # for legacy reasons REDIS_SECRET_KEY = 'SECRET_TOKEN' + REDIS_VALIDATE_SECONDS = 30 + # In Rails secret_key_base is used to encrypt the cookie store # the cookie store contains session data # Discourse also uses this secret key to digest user auth tokens @@ -19,9 +21,21 @@ class GlobalSetting # - generate a token on the fly if needed and cache in redis # - enforce rules about token format falling back to redis if needed def self.safe_secret_key_base + + if @safe_secret_key_base && @token_in_redis && (@token_last_validated + REDIS_VALIDATE_SECONDS) < Time.now + token = $redis.without_namespace.get(REDIS_SECRET_KEY) + if token.nil? + $redis.without_namespace.set(REDIS_SECRET_KEY, @safe_secret_key_base) + end + end + @safe_secret_key_base ||= begin token = secret_key_base if token.blank? || token !~ VALID_SECRET_KEY + + @token_in_redis = true + @token_last_validated = Time.now + token = $redis.without_namespace.get(REDIS_SECRET_KEY) unless token && token =~ VALID_SECRET_KEY token = SecureRandom.hex(64) diff --git a/spec/models/global_setting_spec.rb b/spec/models/global_setting_spec.rb index 7be0db648fd..d3371ce13f4 100644 --- a/spec/models/global_setting_spec.rb +++ b/spec/models/global_setting_spec.rb @@ -2,6 +2,27 @@ require 'rails_helper' require 'tempfile' describe GlobalSetting do + + describe '.safe_secret_key_base' do + it 'sets redis token if it is somehow flushed after 30 seconds' do + token = GlobalSetting.safe_secret_key_base + $redis.without_namespace.del(GlobalSetting::REDIS_SECRET_KEY) + freeze_time 20.seconds.from_now + + GlobalSetting.safe_secret_key_base + new_token = $redis.without_namespace.get(GlobalSetting::REDIS_SECRET_KEY) + expect(new_token).to eq(nil) + + freeze_time 31.seconds.from_now + + GlobalSetting.safe_secret_key_base + + new_token = $redis.without_namespace.get(GlobalSetting::REDIS_SECRET_KEY) + expect(new_token).to eq(token) + + end + end + describe '.redis_config' do describe 'when slave config is not present' do it "should not set any connector" do