# frozen_string_literal: true# :markup: markdownmoduleActionController# :nodoc:moduleAllowBrowserextendActiveSupport::ConcernmoduleClassMethods# Specify the browser versions that will be allowed to access all actions (or# some, as limited by `only:` or `except:`). Only browsers matched in the hash# or named set passed to `versions:` will be blocked if they're below the# versions specified. This means that all other browsers, as well as agents that# aren't reporting a user-agent header, will be allowed access.## A browser that's blocked will by default be served the file in# public/406-unsupported-browser.html with a HTTP status code of "406 Not# Acceptable".## In addition to specifically named browser versions, you can also pass# `:modern` as the set to restrict support to browsers natively supporting webp# images, web push, badges, import maps, CSS nesting, and CSS :has. This# includes Safari 17.2+, Chrome 120+, Firefox 121+, Opera 106+.## You can use https://caniuse.com to check for browser versions supporting the# features you use.## You can use `ActiveSupport::Notifications` to subscribe to events of browsers# being blocked using the `browser_block.action_controller` event name.## Examples:## class ApplicationController < ActionController::Base# # Allow only browsers natively supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has# allow_browser versions: :modern# end## class ApplicationController < ActionController::Base# # Allow only browsers natively supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has# allow_browser versions: :modern, block: :handle_outdated_browser## private# def handle_outdated_browser# render file: Rails.root.join("public/custom-error.html"), status: :not_acceptable# end# end## class ApplicationController < ActionController::Base# # All versions of Chrome and Opera will be allowed, but no versions of "internet explorer" (ie). Safari needs to be 16.4+ and Firefox 121+.# allow_browser versions: { safari: 16.4, firefox: 121, ie: false }# end## class MessagesController < ApplicationController# # In addition to the browsers blocked by ApplicationController, also block Opera below 104 and Chrome below 119 for the show action.# allow_browser versions: { opera: 104, chrome: 119 }, only: :show# enddefallow_browser(versions:,block: ->{renderfile: Rails.root.join("public/406-unsupported-browser.html"),layout: false,status: :not_acceptable},**options)before_action->{allow_browser(versions: versions,block: block)},**optionsendendprivatedefallow_browser(versions:,block:)require"useragent"ifBrowserBlocker.new(request,versions: versions).blocked?ActiveSupport::Notifications.instrument("browser_block.action_controller",request: request,versions: versions)doblock.is_a?(Symbol)?send(block):instance_exec(&block)endendendclassBrowserBlocker# :nodoc:SETS={modern: {safari: 17.2,chrome: 120,firefox: 121,opera: 106,ie: false}}attr_reader:request,:versionsdefinitialize(request,versions:)@request,@versions=request,versionsenddefblocked?user_agent_version_reported?&&unsupported_browser?endprivatedefparsed_user_agent@parsed_user_agent||=UserAgent.parse(request.user_agent)enddefuser_agent_version_reported?request.user_agent.present?&&parsed_user_agent.version.to_s.present?enddefunsupported_browser?version_guarded_browser?&&version_below_minimum_required?&&!bot?enddefversion_guarded_browser?minimum_browser_version_for_browser!=nilenddefbot?parsed_user_agent.bot?enddefversion_below_minimum_required?ifminimum_browser_version_for_browserparsed_user_agent.version<UserAgent::Version.new(minimum_browser_version_for_browser.to_s)elsetrueendenddefminimum_browser_version_for_browserexpanded_versions[normalized_browser_name]enddefexpanded_versions@expanded_versions||=(SETS[versions]||versions).with_indifferent_accessenddefnormalized_browser_namecasename=parsed_user_agent.browser.downcasewhen"internet explorer"then"ie"elsenameendendendendend