Files
danbooru/app/logical/session_loader.rb
evazion d99985160a api keys: add API key usage tracking.
Track when an API key was last used, which IP address last used it, and
how many times it's been used overall.

This is so you can tell when an API key was last used, so you know if
the key is safe to delete, and so you can tell if an unrecognized IP has
used your key.
2021-02-14 21:02:07 -06:00

146 lines
4.2 KiB
Ruby

class SessionLoader
class AuthenticationFailure < StandardError; end
attr_reader :session, :request, :params
def initialize(request)
@request = request
@session = request.session
@params = request.parameters
end
def login(name, password)
user = User.find_by_name(name)
if user.present? && user.authenticate_password(password)
session[:user_id] = user.id
UserEvent.build_from_request(user, :login, request)
user.last_logged_in_at = Time.now
user.last_ip_addr = request.remote_ip
user.save!
user
elsif user.nil?
nil # username incorrect
else
UserEvent.create_from_request!(user, :failed_login, request)
nil # password incorrect
end
end
def logout
session.delete(:user_id)
return if CurrentUser.user.is_anonymous?
UserEvent.create_from_request!(CurrentUser.user, :logout, request)
end
def load
CurrentUser.user = User.anonymous
CurrentUser.ip_addr = request.remote_ip
if has_api_authentication?
load_session_for_api
elsif params[:signed_user_id]
load_param_user(params[:signed_user_id])
elsif session[:user_id]
load_session_user
end
set_statement_timeout
update_last_logged_in_at
update_last_ip_addr
set_time_zone
set_country
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[:api_key].is_a?(String))
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])
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, key)
user, api_key = User.find_by_name(name)&.authenticate_api_key(key)
raise AuthenticationFailure if user.blank?
update_api_key(api_key)
raise User::PrivilegeError if !api_key.has_permission?(request.remote_ip, request.params[:controller], request.params[:action])
CurrentUser.user = user
end
# XXX use rails 6.1 signed ids (https://github.com/rails/rails/blob/6-1-stable/activerecord/CHANGELOG.md)
def load_param_user(signed_user_id)
session[:user_id] = Danbooru::MessageVerifier.new(:login).verify(signed_user_id)
load_session_user
end
def load_session_user
user = User.find_by_id(session[:user_id])
CurrentUser.user = user if user
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.hour.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 update_api_key(api_key)
api_key.increment!(:uses, touch: :last_used_at)
api_key.update!(last_ip_address: request.remote_ip)
end
def set_time_zone
Time.zone = CurrentUser.user.time_zone
end
# Depends on Cloudflare
# https://support.cloudflare.com/hc/en-us/articles/200168236-Configuring-Cloudflare-IP-Geolocation
def set_country
CurrentUser.country = request.headers["CF-IPCountry"]
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
end
end