Extract class from CSP configuration/initialization (#26905)
This commit is contained in:
		
							parent
							
								
									2e6bf60f15
								
							
						
					
					
						commit
						eae5c7334a
					
				| 
						 | 
					@ -0,0 +1,59 @@
 | 
				
			||||||
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ContentSecurityPolicy
 | 
				
			||||||
 | 
					  def base_host
 | 
				
			||||||
 | 
					    Rails.configuration.x.web_domain
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def assets_host
 | 
				
			||||||
 | 
					    url_from_configured_asset_host || url_from_base_host
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def media_host
 | 
				
			||||||
 | 
					    cdn_host_value || assets_host
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def url_from_configured_asset_host
 | 
				
			||||||
 | 
					    Rails.configuration.action_controller.asset_host
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def cdn_host_value
 | 
				
			||||||
 | 
					    s3_alias_host || s3_cloudfront_host || azure_alias_host || s3_hostname_host
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def url_from_base_host
 | 
				
			||||||
 | 
					    host_to_url(base_host)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def host_to_url(host_string)
 | 
				
			||||||
 | 
					    uri_from_configuration_and_string(host_string) if host_string.present?
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def s3_alias_host
 | 
				
			||||||
 | 
					    host_to_url ENV.fetch('S3_ALIAS_HOST', nil)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def s3_cloudfront_host
 | 
				
			||||||
 | 
					    host_to_url ENV.fetch('S3_CLOUDFRONT_HOST', nil)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def azure_alias_host
 | 
				
			||||||
 | 
					    host_to_url ENV.fetch('AZURE_ALIAS_HOST', nil)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def s3_hostname_host
 | 
				
			||||||
 | 
					    host_to_url ENV.fetch('S3_HOSTNAME', nil)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def uri_from_configuration_and_string(host_string)
 | 
				
			||||||
 | 
					    Addressable::URI.parse("#{host_protocol}://#{host_string}").tap do |uri|
 | 
				
			||||||
 | 
					      uri.path += '/' unless uri.path.blank? || uri.path.end_with?('/')
 | 
				
			||||||
 | 
					    end.to_s
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def host_protocol
 | 
				
			||||||
 | 
					    Rails.configuration.x.use_https ? 'https' : 'http'
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
| 
						 | 
					@ -6,24 +6,11 @@
 | 
				
			||||||
# See the Securing Rails Applications Guide for more information:
 | 
					# See the Securing Rails Applications Guide for more information:
 | 
				
			||||||
# https://guides.rubyonrails.org/security.html#content-security-policy-header
 | 
					# https://guides.rubyonrails.org/security.html#content-security-policy-header
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def host_to_url(str)
 | 
					require_relative '../../app/lib/content_security_policy'
 | 
				
			||||||
  return if str.blank?
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  uri = Addressable::URI.parse("http#{Rails.configuration.x.use_https ? 's' : ''}://#{str}")
 | 
					policy = ContentSecurityPolicy.new
 | 
				
			||||||
  uri.path += '/' unless uri.path.blank? || uri.path.end_with?('/')
 | 
					assets_host = policy.assets_host
 | 
				
			||||||
  uri.to_s
 | 
					media_host = policy.media_host
 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
base_host = Rails.configuration.x.web_domain
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
assets_host   = Rails.configuration.action_controller.asset_host
 | 
					 | 
				
			||||||
assets_host ||= host_to_url(base_host)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
media_host   = host_to_url(ENV['S3_ALIAS_HOST'])
 | 
					 | 
				
			||||||
media_host ||= host_to_url(ENV['S3_CLOUDFRONT_HOST'])
 | 
					 | 
				
			||||||
media_host ||= host_to_url(ENV['AZURE_ALIAS_HOST'])
 | 
					 | 
				
			||||||
media_host ||= host_to_url(ENV['S3_HOSTNAME']) if ENV['S3_ENABLED'] == 'true'
 | 
					 | 
				
			||||||
media_host ||= assets_host
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
def sso_host
 | 
					def sso_host
 | 
				
			||||||
  return unless ENV['ONE_CLICK_SSO_LOGIN'] == 'true'
 | 
					  return unless ENV['ONE_CLICK_SSO_LOGIN'] == 'true'
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,129 @@
 | 
				
			||||||
 | 
					# frozen_string_literal: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require 'rails_helper'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe ContentSecurityPolicy do
 | 
				
			||||||
 | 
					  subject { described_class.new }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  around do |example|
 | 
				
			||||||
 | 
					    original_asset_host = Rails.configuration.action_controller.asset_host
 | 
				
			||||||
 | 
					    original_web_domain = Rails.configuration.x.web_domain
 | 
				
			||||||
 | 
					    original_use_https = Rails.configuration.x.use_https
 | 
				
			||||||
 | 
					    example.run
 | 
				
			||||||
 | 
					    Rails.configuration.action_controller.asset_host = original_asset_host
 | 
				
			||||||
 | 
					    Rails.configuration.x.web_domain = original_web_domain
 | 
				
			||||||
 | 
					    Rails.configuration.x.use_https = original_use_https
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe '#base_host' do
 | 
				
			||||||
 | 
					    before { Rails.configuration.x.web_domain = 'host.example' }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it 'returns the configured value for the web domain' do
 | 
				
			||||||
 | 
					      expect(subject.base_host).to eq 'host.example'
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe '#assets_host' do
 | 
				
			||||||
 | 
					    context 'when asset_host is not configured' do
 | 
				
			||||||
 | 
					      before { Rails.configuration.action_controller.asset_host = nil }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      context 'with a configured web domain' do
 | 
				
			||||||
 | 
					        before { Rails.configuration.x.web_domain = 'host.example' }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        context 'when use_https is enabled' do
 | 
				
			||||||
 | 
					          before { Rails.configuration.x.use_https = true }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          it 'returns value from base host with https protocol' do
 | 
				
			||||||
 | 
					            expect(subject.assets_host).to eq 'https://host.example'
 | 
				
			||||||
 | 
					          end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        context 'when use_https is disabled' do
 | 
				
			||||||
 | 
					          before { Rails.configuration.x.use_https = false }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          it 'returns value from base host with http protocol' do
 | 
				
			||||||
 | 
					            expect(subject.assets_host).to eq 'http://host.example'
 | 
				
			||||||
 | 
					          end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context 'when asset_host is configured' do
 | 
				
			||||||
 | 
					      before do
 | 
				
			||||||
 | 
					        Rails.configuration.action_controller.asset_host = 'https://assets.host.example'
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'returns full value from configured host' do
 | 
				
			||||||
 | 
					        expect(subject.assets_host).to eq 'https://assets.host.example'
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe '#media_host' do
 | 
				
			||||||
 | 
					    context 'when there is no configured CDN' do
 | 
				
			||||||
 | 
					      it 'defaults to using the assets_host value' do
 | 
				
			||||||
 | 
					        expect(subject.media_host).to eq(subject.assets_host)
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context 'when an S3 alias host is configured' do
 | 
				
			||||||
 | 
					      around do |example|
 | 
				
			||||||
 | 
					        ClimateControl.modify S3_ALIAS_HOST: 'asset-host.s3-alias.example' do
 | 
				
			||||||
 | 
					          example.run
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'uses the s3 alias host value' do
 | 
				
			||||||
 | 
					        expect(subject.media_host).to eq 'https://asset-host.s3-alias.example'
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context 'when an S3 alias host with a trailing path is configured' do
 | 
				
			||||||
 | 
					      around do |example|
 | 
				
			||||||
 | 
					        ClimateControl.modify S3_ALIAS_HOST: 'asset-host.s3-alias.example/pathname' do
 | 
				
			||||||
 | 
					          example.run
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'uses the s3 alias host value and preserves the path' do
 | 
				
			||||||
 | 
					        expect(subject.media_host).to eq 'https://asset-host.s3-alias.example/pathname/'
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context 'when an S3 cloudfront host is configured' do
 | 
				
			||||||
 | 
					      around do |example|
 | 
				
			||||||
 | 
					        ClimateControl.modify S3_CLOUDFRONT_HOST: 'asset-host.s3-cloudfront.example' do
 | 
				
			||||||
 | 
					          example.run
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'uses the s3 cloudfront host value' do
 | 
				
			||||||
 | 
					        expect(subject.media_host).to eq 'https://asset-host.s3-cloudfront.example'
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context 'when an azure alias host is configured' do
 | 
				
			||||||
 | 
					      around do |example|
 | 
				
			||||||
 | 
					        ClimateControl.modify AZURE_ALIAS_HOST: 'asset-host.azure-alias.example' do
 | 
				
			||||||
 | 
					          example.run
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'uses the azure alias host value' do
 | 
				
			||||||
 | 
					        expect(subject.media_host).to eq 'https://asset-host.azure-alias.example'
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context 'when s3_enabled is configured' do
 | 
				
			||||||
 | 
					      around do |example|
 | 
				
			||||||
 | 
					        ClimateControl.modify S3_ENABLED: 'true', S3_HOSTNAME: 'asset-host.s3.example' do
 | 
				
			||||||
 | 
					          example.run
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'uses the s3 hostname host value' do
 | 
				
			||||||
 | 
					        expect(subject.media_host).to eq 'https://asset-host.s3.example'
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
		Loading…
	
		Reference in New Issue