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 # TODO remove this stuff in 2017 was only added to smoothen the upgrade process def self.has_auth_token_updated_at? (@has_auth_token_updated_at ||= User.column_names.include?("auth_token_updated_at") ? :true : :false ) == :true end # 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 if ::Auth::DefaultCurrentUserProvider.has_auth_token_updated_at? current_user = User.find_by("auth_token = ? AND (auth_token_updated_at IS NULL OR auth_token_updated_at > ?)", auth_token, SiteSetting.maximum_session_age.hours.ago) else current_user = User.find_by(auth_token: auth_token) end 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 && ::Auth::DefaultCurrentUserProvider.has_auth_token_updated_at? && (!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