forked from rails/rails
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add allow_browser to set minimum versions for your application (rails…
…#50505) * Add allow_browser to set minimum versions for your application
- Loading branch information
Showing
12 changed files
with
258 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
111 changes: 111 additions & 0 deletions
111
actionpack/lib/action_controller/metal/allow_browser.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
# frozen_string_literal: true | ||
|
||
module ActionController # :nodoc: | ||
module AllowBrowser | ||
extend ActiveSupport::Concern | ||
|
||
module ClassMethods | ||
# Specify the browser versions that will be allowed to access all actions (or some, as limited by <tt>only:</tt> or <tt>except:</tt>). | ||
# Only browsers matched in the hash or named set passed to <tt>versions:</tt> 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/426.html with a HTTP status code of "426 Upgrade Required". | ||
# | ||
# In addition to specifically named browser versions, you can also pass <tt>:modern</tt> 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 119+, Firefox 121+, Opera 104+. | ||
# | ||
# 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 + :has | ||
# allow_browser versions: :modern | ||
# 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 | ||
# end | ||
def allow_browser(versions:, block: -> { render file: Rails.root.join("public/426.html"), layout: false, status: :upgrade_required }, **options) | ||
before_action -> { allow_browser(versions: versions, block: block) }, **options | ||
end | ||
end | ||
|
||
private | ||
def allow_browser(versions:, block:) | ||
require "useragent" | ||
|
||
if BrowserBlocker.new(request, versions: versions).blocked? | ||
ActiveSupport::Notifications.instrument("browser_block.action_controller", request: request, versions: versions) do | ||
instance_exec(&block) | ||
end | ||
end | ||
end | ||
|
||
class BrowserBlocker | ||
SETS = { | ||
modern: { safari: 17.2, chrome: 119, firefox: 121, opera: 104, ie: false } | ||
} | ||
|
||
attr_reader :request, :versions | ||
|
||
def initialize(request, versions:) | ||
@request, @versions = request, versions | ||
end | ||
|
||
def blocked? | ||
user_agent_version_reported? && unsupported_browser? | ||
end | ||
|
||
private | ||
def parsed_user_agent | ||
@parsed_user_agent ||= UserAgent.parse(request.user_agent) | ||
end | ||
|
||
def user_agent_version_reported? | ||
request.user_agent.present? && parsed_user_agent.version.to_s.present? | ||
end | ||
|
||
def unsupported_browser? | ||
version_guarded_browser? && version_below_minimum_required? | ||
end | ||
|
||
def version_guarded_browser? | ||
minimum_browser_version_for_browser != nil | ||
end | ||
|
||
def version_below_minimum_required? | ||
if minimum_browser_version_for_browser | ||
parsed_user_agent.version < UserAgent::Version.new(minimum_browser_version_for_browser.to_s) | ||
else | ||
true | ||
end | ||
end | ||
|
||
def minimum_browser_version_for_browser | ||
expanded_versions[normalized_browser_name] | ||
end | ||
|
||
def expanded_versions | ||
@expanded_versions ||= (SETS[versions] || versions).with_indifferent_access | ||
end | ||
|
||
def normalized_browser_name | ||
case name = parsed_user_agent.browser.downcase | ||
when "internet explorer" then "ie" | ||
else name | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
# frozen_string_literal: true | ||
|
||
require "abstract_unit" | ||
|
||
class AllowBrowserController < ActionController::Base | ||
allow_browser versions: { safari: "16.4", chrome: "119", firefox: "123", opera: "104", ie: false }, block: -> { head :upgrade_required }, only: :hello | ||
def hello | ||
head :ok | ||
end | ||
|
||
allow_browser versions: :modern, block: -> { head :upgrade_required }, only: :modern | ||
def modern | ||
head :ok | ||
end | ||
end | ||
|
||
class AllowBrowserTest < ActionController::TestCase | ||
tests AllowBrowserController | ||
|
||
CHROME_118 = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118 Safari/537.36" | ||
CHROME_120 = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120 Safari/537.36" | ||
SAFARI_17_2_0 = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2.0 Safari/605.1.15" | ||
FIREFOX_114 = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/114.0" | ||
IE_11 = "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" | ||
OPERA_104 = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36 OPR/104.0.4638.54" | ||
|
||
test "blocked browser below version limit" do | ||
get_with_agent :hello, FIREFOX_114 | ||
assert_response :upgrade_required | ||
end | ||
|
||
test "blocked browser by name" do | ||
get_with_agent :hello, IE_11 | ||
assert_response :upgrade_required | ||
end | ||
|
||
test "allowed browsers above specific version limit" do | ||
get_with_agent :hello, SAFARI_17_2_0 | ||
assert_response :ok | ||
|
||
get_with_agent :hello, CHROME_120 | ||
assert_response :ok | ||
|
||
get_with_agent :hello, OPERA_104 | ||
assert_response :ok | ||
end | ||
|
||
test "browsers against modern limit" do | ||
get_with_agent :modern, SAFARI_17_2_0 | ||
assert_response :ok | ||
|
||
get_with_agent :modern, CHROME_118 | ||
assert_response :upgrade_required | ||
|
||
get_with_agent :modern, CHROME_120 | ||
assert_response :ok | ||
|
||
get_with_agent :modern, OPERA_104 | ||
assert_response :ok | ||
end | ||
|
||
private | ||
def get_with_agent(action, agent) | ||
@request.headers["User-Agent"] = agent | ||
get action | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 4 additions & 0 deletions
4
...ies/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,6 @@ | ||
class ApplicationController < ActionController::<%= options.api? ? "API" : "Base" %> | ||
<%- unless options.api? -%> | ||
# Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has. | ||
allow_browser versions: :modern | ||
<% end -%> | ||
end |
66 changes: 66 additions & 0 deletions
66
railties/lib/rails/generators/rails/app/templates/public/426.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<title>Your browser is not supported (426)</title> | ||
<meta name="viewport" content="width=device-width,initial-scale=1"> | ||
<style> | ||
.rails-default-error-page { | ||
background-color: #EFEFEF; | ||
color: #2E2F30; | ||
text-align: center; | ||
font-family: arial, sans-serif; | ||
margin: 0; | ||
} | ||
|
||
.rails-default-error-page div.dialog { | ||
width: 95%; | ||
max-width: 33em; | ||
margin: 4em auto 0; | ||
} | ||
|
||
.rails-default-error-page div.dialog > div { | ||
border: 1px solid #CCC; | ||
border-right-color: #999; | ||
border-left-color: #999; | ||
border-bottom-color: #BBB; | ||
border-top: #B00100 solid 4px; | ||
border-top-left-radius: 9px; | ||
border-top-right-radius: 9px; | ||
background-color: white; | ||
padding: 7px 12% 0; | ||
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17); | ||
} | ||
|
||
.rails-default-error-page h1 { | ||
font-size: 100%; | ||
color: #730E15; | ||
line-height: 1.5em; | ||
} | ||
|
||
.rails-default-error-page div.dialog > p { | ||
margin: 0 0 1em; | ||
padding: 1em; | ||
background-color: #F7F7F7; | ||
border: 1px solid #CCC; | ||
border-right-color: #999; | ||
border-left-color: #999; | ||
border-bottom-color: #999; | ||
border-bottom-left-radius: 4px; | ||
border-bottom-right-radius: 4px; | ||
border-top-color: #DADADA; | ||
color: #666; | ||
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17); | ||
} | ||
</style> | ||
</head> | ||
|
||
<body class="rails-default-error-page"> | ||
<!-- This file lives in public/426.html --> | ||
<div class="dialog"> | ||
<div> | ||
<h1>Your browser is not supported.</h1> | ||
<p>Please upgrade your browser to continue.</p> | ||
</div> | ||
</div> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters