discourse/lib/auth/default_current_user_provider.rb
Sam df535c6346 FEATURE: refresh session cookie at most once an hour
This feature ensures session cookie lifespan is extended
when user is online.

Also decreases session timeout from 90 to 60 days.
Ensures all users (including logged on ones) get expiring sessions.
2016-07-25 12:07:31 +10:00

148 lines
4.3 KiB
Ruby

require_dependency "auth/current_user_provider"
class Auth::DefaultCurrentUserProvider
CURRENT_USER_KEY ||= "_DISCOURSE_CURRENT_USER".freeze
API_KEY ||= "api_key".freeze
API_KEY_ENV ||= "_DISCOURSE_API".freeze
TOKEN_COOKIE ||= "_t".freeze
PATH_INFO ||= "PATH_INFO".freeze
# do all current user initialization here
def initialize(env)
@env = env
@request = Rack::Request.new(env)
end
# our current user, return nil if none is found
def current_user
return @env[CURRENT_USER_KEY] if @env.key?(CURRENT_USER_KEY)
# bypass if we have the shared session header
if shared_key = @env['HTTP_X_SHARED_SESSION_KEY']
uid = $redis.get("shared_session_key_#{shared_key}")
user = nil
if uid
user = User.find_by(id: uid.to_i)
end
@env[CURRENT_USER_KEY] = user
return user
end
request = @request
auth_token = request.cookies[TOKEN_COOKIE]
current_user = nil
if auth_token && auth_token.length == 32
current_user = User.where(auth_token: auth_token)
.where('auth_token_updated_at IS NULL OR auth_token_updated_at > ?',
SiteSetting.maximum_session_age.hours.ago)
.first
end
if current_user && (current_user.suspended? || !current_user.active)
current_user = nil
end
if current_user && should_update_last_seen?
u = current_user
Scheduler::Defer.later "Updating Last Seen" do
u.update_last_seen!
u.update_ip_address!(request.ip)
end
end
# possible we have an api call, impersonate
if api_key = request[API_KEY]
current_user = lookup_api_user(api_key, request)
raise Discourse::InvalidAccess unless current_user
@env[API_KEY_ENV] = true
end
@env[CURRENT_USER_KEY] = current_user
end
def refresh_session(user, session, cookies)
if user && (!user.auth_token_updated_at || user.auth_token_updated_at <= 1.hour.ago)
user.update_column(:auth_token_updated_at, Time.zone.now)
cookies[TOKEN_COOKIE] = { value: user.auth_token, httponly: true, expires: SiteSetting.maximum_session_age.hours.from_now }
end
end
def log_on_user(user, session, cookies)
user.auth_token = SecureRandom.hex(16)
user.auth_token_updated_at = Time.zone.now
user.save!
cookies[TOKEN_COOKIE] = { value: user.auth_token, httponly: true, expires: SiteSetting.maximum_session_age.hours.from_now }
make_developer_admin(user)
enable_bootstrap_mode(user)
@env[CURRENT_USER_KEY] = user
end
def make_developer_admin(user)
if user.active? &&
!user.admin &&
Rails.configuration.respond_to?(:developer_emails) &&
Rails.configuration.developer_emails.include?(user.email)
user.admin = true
user.save
end
end
def enable_bootstrap_mode(user)
Jobs.enqueue(:enable_bootstrap_mode, user_id: user.id) if user.admin && user.last_seen_at.nil? && !SiteSetting.bootstrap_mode_enabled && user.is_singular_admin?
end
def log_off_user(session, cookies)
if SiteSetting.log_out_strict && (user = current_user)
user.auth_token = nil
user.save!
if user.admin && defined?(Rack::MiniProfiler)
# clear the profiling cookie to keep stuff tidy
cookies["__profilin"] = nil
end
user.logged_out
end
cookies[TOKEN_COOKIE] = nil
end
# api has special rights return true if api was detected
def is_api?
current_user
@env[API_KEY_ENV]
end
def has_auth_cookie?
cookie = @request.cookies[TOKEN_COOKIE]
!cookie.nil? && cookie.length == 32
end
def should_update_last_seen?
!(@request.path =~ /^\/message-bus\//)
end
protected
def lookup_api_user(api_key_value, request)
if api_key = ApiKey.where(key: api_key_value).includes(:user).first
api_username = request["api_username"]
if api_key.allowed_ips.present? && !api_key.allowed_ips.any? { |ip| ip.include?(request.ip) }
Rails.logger.warn("[Unauthorized API Access] username: #{api_username}, IP address: #{request.ip}")
return nil
end
if api_key.user
api_key.user if !api_username || (api_key.user.username_lower == api_username.downcase)
elsif api_username
User.find_by(username_lower: api_username.downcase)
end
end
end
end