lib/airbrake/rack/user.rb
# frozen_string_literal: true
module Airbrake
module Rack
# Represents an authenticated user, which can be converted to Airbrake's
# payload format. Supports Warden and Omniauth authentication frameworks.
class User
# Finds the user in the Rack environment and creates a new user wrapper.
#
# @param [Hash{String=>Object}] rack_env The Rack environment
# @return [Airbrake::Rack::User, nil]
def self.extract(rack_env)
# Warden support (including Devise).
if (warden = rack_env['warden'])
user = warden.user(run_callbacks: false)
# Early return to prevent unwanted possible authentication via
# calling the `current_user` method later.
# See: https://github.com/airbrake/airbrake/issues/641
return user ? new(user) : nil
end
# Fallback mode (OmniAuth support included). Works only for Rails.
user = try_current_user(rack_env)
new(user) if user
end
def self.try_current_user(rack_env)
controller = rack_env['action_controller.instance']
return unless controller.respond_to?(:current_user, true)
return unless [-1, 0].include?(controller.method(:current_user).arity)
begin
controller.__send__(:current_user)
rescue Exception => _e # rubocop:disable Lint/RescueException
nil
end
end
private_class_method :try_current_user
def initialize(user)
@user = user
end
def as_json
user = {}
user[:id] = try_to_get(:id)
user[:name] = full_name
user[:username] = try_to_get(:username)
user[:email] = try_to_get(:email)
user = user.delete_if { |_key, val| val.nil? }
user.empty? ? user : { user: user }
end
private
def try_to_get(key)
return unless @user.respond_to?(key)
# try methods with no arguments or with variable number of arguments,
# where none of them are required
return unless @user.method(key).arity.between?(-1, 0)
String(@user.__send__(key))
end
def full_name
# Try to get first and last names. If that fails, try to get just 'name'.
name = [try_to_get(:first_name), try_to_get(:last_name)].compact.join(' ')
name.empty? ? try_to_get(:name) : name
end
end
end
end