mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-24 20:36:49 +00:00
Merge pull request #2387 from Matt-Yorkley/embedded_referrers
Embedded response headers
This commit is contained in:
@@ -56,48 +56,9 @@ class ApplicationController < ActionController::Base
|
||||
end
|
||||
|
||||
def enable_embedded_shopfront
|
||||
return unless embeddable?
|
||||
return if embedding_without_https?
|
||||
|
||||
response.headers.delete 'X-Frame-Options'
|
||||
response.headers['Content-Security-Policy'] = "frame-ancestors #{URI(request.referer).host.downcase}"
|
||||
|
||||
check_embedded_request
|
||||
set_embedded_layout
|
||||
end
|
||||
|
||||
def embedded_shopfront_referer
|
||||
return if request.referer.blank?
|
||||
domain = URI(request.referer).host.downcase
|
||||
domain.start_with?('www.') ? domain[4..-1] : domain
|
||||
end
|
||||
|
||||
def embeddable?
|
||||
whitelist = Spree::Config[:embedded_shopfronts_whitelist]
|
||||
domain = embedded_shopfront_referer
|
||||
Spree::Config[:enable_embedded_shopfronts] && whitelist.present? && domain.present? && whitelist.include?(domain)
|
||||
end
|
||||
|
||||
def embedding_without_https?
|
||||
request.referer && URI(request.referer).scheme != 'https' && !Rails.env.test? && !Rails.env.development?
|
||||
end
|
||||
|
||||
def check_embedded_request
|
||||
return unless params[:embedded_shopfront]
|
||||
|
||||
# Show embedded shopfront CSS
|
||||
session[:embedded_shopfront] = true
|
||||
|
||||
# Get shopfront slug and set redirect path
|
||||
if params[:controller] == 'enterprises' && params[:action] == 'shop' && params[:id]
|
||||
slug = params[:id]
|
||||
session[:shopfront_redirect] = '/' + slug + '/shop?embedded_shopfront=true'
|
||||
end
|
||||
end
|
||||
|
||||
def set_embedded_layout
|
||||
return unless session[:embedded_shopfront]
|
||||
@shopfront_layout = 'embedded'
|
||||
embed_service = EmbeddedPageService.new(params, session, request, response)
|
||||
embed_service.embed!
|
||||
@shopfront_layout = 'embedded' if embed_service.use_embedded_layout?
|
||||
end
|
||||
|
||||
def action
|
||||
|
||||
92
app/services/embedded_page_service.rb
Normal file
92
app/services/embedded_page_service.rb
Normal file
@@ -0,0 +1,92 @@
|
||||
# Processes requests for pages embedded in iframes
|
||||
|
||||
class EmbeddedPageService
|
||||
def initialize(params, session, request, response)
|
||||
@params = params
|
||||
@session = session
|
||||
@request = request
|
||||
@response = response
|
||||
|
||||
@embedding_domain = @session[:embedding_domain]
|
||||
@use_embedded_layout = false
|
||||
end
|
||||
|
||||
def embed!
|
||||
return unless embeddable?
|
||||
return if embedding_without_https?
|
||||
|
||||
process_embedded_request
|
||||
set_response_headers
|
||||
set_embedded_layout
|
||||
end
|
||||
|
||||
def use_embedded_layout?
|
||||
@use_embedded_layout
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def embeddable?
|
||||
return true if current_referer == @request.host
|
||||
|
||||
domain = current_referer_without_www
|
||||
whitelist = Spree::Config[:embedded_shopfronts_whitelist]
|
||||
|
||||
embedding_enabled? && whitelist.present? && domain.present? && whitelist.include?(domain)
|
||||
end
|
||||
|
||||
def embedding_without_https?
|
||||
@request.referer && URI(@request.referer).scheme != 'https' && !Rails.env.test? && !Rails.env.development?
|
||||
end
|
||||
|
||||
def process_embedded_request
|
||||
return unless @params[:embedded_shopfront]
|
||||
|
||||
set_embedding_domain
|
||||
|
||||
@session[:embedded_shopfront] = true
|
||||
set_logout_redirect
|
||||
end
|
||||
|
||||
def set_response_headers
|
||||
@response.headers.delete 'X-Frame-Options'
|
||||
@response.headers['Content-Security-Policy'] = "frame-ancestors 'self' #{@embedding_domain}"
|
||||
end
|
||||
|
||||
def set_embedding_domain
|
||||
return unless @params[:embedded_shopfront]
|
||||
return if current_referer == @request.host
|
||||
|
||||
@embedding_domain = current_referer
|
||||
@session[:embedding_domain] = current_referer
|
||||
end
|
||||
|
||||
def set_logout_redirect
|
||||
return unless enterprise_slug
|
||||
@session[:shopfront_redirect] = '/' + enterprise_slug + '/shop?embedded_shopfront=true'
|
||||
end
|
||||
|
||||
def enterprise_slug
|
||||
return false unless @params[:controller] == 'enterprises' && @params[:action] == 'shop' && @params[:id]
|
||||
@params[:id]
|
||||
end
|
||||
|
||||
def current_referer
|
||||
return if @request.referer.blank?
|
||||
URI(@request.referer).host.downcase
|
||||
end
|
||||
|
||||
def current_referer_without_www
|
||||
return unless current_referer
|
||||
current_referer.start_with?('www.') ? current_referer[4..-1] : current_referer
|
||||
end
|
||||
|
||||
def set_embedded_layout
|
||||
return unless @session[:embedded_shopfront]
|
||||
@use_embedded_layout = true
|
||||
end
|
||||
|
||||
def embedding_enabled?
|
||||
Spree::Config[:enable_embedded_shopfronts]
|
||||
end
|
||||
end
|
||||
@@ -48,11 +48,11 @@ describe "setting response headers for embedded shopfronts", type: :request do
|
||||
end
|
||||
|
||||
it "allows iframes on certain pages when enabled in configuration" do
|
||||
get shops_path
|
||||
get enterprise_shop_path(enterprise) + '?embedded_shopfront=true'
|
||||
|
||||
expect(response.status).to be 200
|
||||
expect(response.headers['X-Frame-Options']).to be_nil
|
||||
expect(response.headers['Content-Security-Policy']).to eq "frame-ancestors external-site.com"
|
||||
expect(response.headers['Content-Security-Policy']).to eq "frame-ancestors 'self' external-site.com"
|
||||
|
||||
get spree.admin_path
|
||||
|
||||
@@ -69,11 +69,11 @@ describe "setting response headers for embedded shopfronts", type: :request do
|
||||
end
|
||||
|
||||
it "matches the URL structure in the header" do
|
||||
get shops_path
|
||||
get enterprise_shop_path(enterprise) + '?embedded_shopfront=true'
|
||||
|
||||
expect(response.status).to be 200
|
||||
expect(response.headers['X-Frame-Options']).to be_nil
|
||||
expect(response.headers['Content-Security-Policy']).to eq "frame-ancestors www.external-site.com"
|
||||
expect(response.headers['Content-Security-Policy']).to eq "frame-ancestors 'self' www.external-site.com"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
63
spec/services/embedded_page_service_spec.rb
Normal file
63
spec/services/embedded_page_service_spec.rb
Normal file
@@ -0,0 +1,63 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe EmbeddedPageService do
|
||||
let(:enterprise_slug) { 'test-enterprise' }
|
||||
let(:params) { { controller: 'enterprises', action: 'shop', id: enterprise_slug, embedded_shopfront: true } }
|
||||
let(:session) { {} }
|
||||
let(:request) { ActionController::TestRequest.new('HTTP_HOST' => 'ofn-instance.com', 'HTTP_REFERER' => 'https://embedding-enterprise.com') }
|
||||
let(:response) { ActionController::TestResponse.new(200, 'X-Frame-Options' => 'DENY', 'Content-Security-Policy' => "frame-ancestors 'none'") }
|
||||
let(:service) { EmbeddedPageService.new(params, session, request, response) }
|
||||
|
||||
before do
|
||||
Spree::Config.set(
|
||||
enable_embedded_shopfronts: true,
|
||||
embedded_shopfronts_whitelist: 'embedding-enterprise.com example.com'
|
||||
)
|
||||
end
|
||||
|
||||
describe "processing embedded page requests" do
|
||||
context "when the request's referer is in the whitelist" do
|
||||
before { service.embed! }
|
||||
|
||||
it "sets the response headers to enables embedding requests from the embedding site" do
|
||||
expect(response.headers).to_not include 'X-Frame-Options' => 'DENY'
|
||||
expect(response.headers).to include 'Content-Security-Policy' => "frame-ancestors 'self' embedding-enterprise.com"
|
||||
end
|
||||
|
||||
it "sets session variables" do
|
||||
expect(session[:embedded_shopfront]).to eq true
|
||||
expect(session[:embedding_domain]).to eq 'embedding-enterprise.com'
|
||||
expect(session[:shopfront_redirect]).to eq '/' + enterprise_slug + '/shop?embedded_shopfront=true'
|
||||
end
|
||||
|
||||
it "publicly reports that embedded layout should be used" do
|
||||
expect(service.use_embedded_layout?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context "when embedding is enabled for a different site in the current session" do
|
||||
before do
|
||||
session[:embedding_domain] = 'another-enterprise.com'
|
||||
session[:shopfront_redirect] = '/another-enterprise/shop?embedded_shopfront=true'
|
||||
service.embed!
|
||||
end
|
||||
|
||||
it "resets the session variables for the new request" do
|
||||
expect(session[:embedded_shopfront]).to eq true
|
||||
expect(session[:embedding_domain]).to eq 'embedding-enterprise.com'
|
||||
expect(session[:shopfront_redirect]).to eq '/' + enterprise_slug + '/shop?embedded_shopfront=true'
|
||||
end
|
||||
end
|
||||
|
||||
context "when the request's referer is not in the whitelist" do
|
||||
before do
|
||||
Spree::Config.set(embedded_shopfronts_whitelist: 'example.com')
|
||||
service.embed!
|
||||
end
|
||||
|
||||
it "does not enable embedding" do
|
||||
expect(response.headers['X-Frame-Options']).to eq 'DENY'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user