Remove the "Remember" checkbox from the login page. Make session cookies permanent instead. Phase out legacy `user_name` and `password_hash` cookies. Previously a user's session cookies would be cleared whenever they closed their browser window, which would log them out of the site. To work around this, when the "Remember" box was checked on the login page (which it was by default), the user's name and password hash (!) would be stored in separate permanent cookies, which would be used to automatically log the user back in when their session cookies were cleared. We can avoid all of this just by making the session cookies themselves permanent.
126 lines
3.6 KiB
Ruby
126 lines
3.6 KiB
Ruby
class SessionLoader
|
|
class AuthenticationFailure < Exception ; end
|
|
|
|
attr_reader :session, :cookies, :request, :params
|
|
|
|
def initialize(request)
|
|
@request = request
|
|
@session = request.session
|
|
@cookies = request.cookie_jar
|
|
@params = request.parameters
|
|
end
|
|
|
|
def load
|
|
CurrentUser.user = User.anonymous
|
|
CurrentUser.ip_addr = request.remote_ip
|
|
|
|
if has_api_authentication?
|
|
load_session_for_api
|
|
elsif session[:user_id]
|
|
load_session_user
|
|
elsif cookie_password_hash_valid?
|
|
load_cookie_user
|
|
end
|
|
|
|
set_statement_timeout
|
|
update_last_logged_in_at
|
|
update_last_ip_addr
|
|
set_time_zone
|
|
set_safe_mode
|
|
initialize_session_cookies
|
|
CurrentUser.user.unban! if CurrentUser.user.ban_expired?
|
|
ensure
|
|
DanbooruLogger.add_session_attributes(request, session, CurrentUser.user)
|
|
end
|
|
|
|
def has_api_authentication?
|
|
request.authorization.present? || params[:login].present? || params[:api_key].present? || params[:password_hash].present?
|
|
end
|
|
|
|
private
|
|
|
|
def set_statement_timeout
|
|
timeout = CurrentUser.user.statement_timeout
|
|
ActiveRecord::Base.connection.execute("set statement_timeout = #{timeout}")
|
|
end
|
|
|
|
def load_session_for_api
|
|
if request.authorization
|
|
authenticate_basic_auth
|
|
elsif params[:login].present? && params[:api_key].present?
|
|
authenticate_api_key(params[:login], params[:api_key])
|
|
elsif params[:login].present? && params[:password_hash].present?
|
|
authenticate_legacy_api_key(params[:login], params[:password_hash])
|
|
else
|
|
raise AuthenticationFailure
|
|
end
|
|
end
|
|
|
|
def authenticate_basic_auth
|
|
credentials = ::Base64.decode64(request.authorization.split(' ', 2).last || '')
|
|
login, api_key = credentials.split(/:/, 2)
|
|
DanbooruLogger.add_attributes("request.params", login: login)
|
|
authenticate_api_key(login, api_key)
|
|
end
|
|
|
|
def authenticate_api_key(name, api_key)
|
|
CurrentUser.user = User.authenticate_api_key(name, api_key)
|
|
|
|
if CurrentUser.user.nil?
|
|
raise AuthenticationFailure.new
|
|
end
|
|
end
|
|
|
|
def authenticate_legacy_api_key(name, password_hash)
|
|
CurrentUser.user = User.authenticate_hash(name, password_hash)
|
|
|
|
if CurrentUser.user.nil?
|
|
raise AuthenticationFailure.new
|
|
end
|
|
end
|
|
|
|
def load_session_user
|
|
user = User.find_by_id(session[:user_id])
|
|
CurrentUser.user = user if user
|
|
end
|
|
|
|
def load_cookie_user
|
|
CurrentUser.user = User.find_by_name(cookies.signed[:user_name])
|
|
session[:user_id] = CurrentUser.user.id
|
|
end
|
|
|
|
def cookie_password_hash_valid?
|
|
cookies[:password_hash] && cookies.signed[:user_name] && User.authenticate_cookie_hash(cookies.signed[:user_name], cookies[:password_hash])
|
|
end
|
|
|
|
def update_last_logged_in_at
|
|
return if CurrentUser.is_anonymous?
|
|
return if CurrentUser.last_logged_in_at && CurrentUser.last_logged_in_at > 1.week.ago
|
|
CurrentUser.user.update_attribute(:last_logged_in_at, Time.now)
|
|
end
|
|
|
|
def update_last_ip_addr
|
|
return if CurrentUser.is_anonymous?
|
|
return if CurrentUser.user.last_ip_addr == @request.remote_ip
|
|
CurrentUser.user.update_attribute(:last_ip_addr, @request.remote_ip)
|
|
end
|
|
|
|
def set_time_zone
|
|
Time.zone = CurrentUser.user.time_zone
|
|
end
|
|
|
|
def set_safe_mode
|
|
safe_mode = request.host.match?(/safebooru/i) || params[:safe_mode].to_s.truthy? || CurrentUser.user.enable_safe_mode?
|
|
CurrentUser.safe_mode = safe_mode
|
|
end
|
|
|
|
def initialize_session_cookies
|
|
session.options[:expire_after] = 20.years
|
|
session[:started_at] ||= Time.now.utc.to_s
|
|
|
|
# clear out legacy login cookies if present
|
|
cookies.delete(:user_name)
|
|
cookies.delete(:password_hash)
|
|
end
|
|
end
|