-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow for dynamic hosts in CORS origin config (#409)
## Status - Closes #405 ## What's changed? This extends the current origin-setting functionality (literal origins set via a comma-separated environment variable) and allows the env var string to also contain regular expressions. These allow the origins to match dynamic patterns (eg. `https://*.projects-ui.pages.dev`) and ensures that configuration still remains in the environment as we don't want to assume that anyone running this application will want the same allowed origins configured as us (other than for `localhost` for local and test environments only). eg. instead of currently having to set the following in `ALLOWED_ORIGINS`: ``` https://foo.raspberrypi.org, https://bar.raspberrypi.org, https://foo.editor-standalone-eyq.pages.dev, https://bar.editor-standalone-eyq.pages.dev ``` The following can be set: ``` /https:\/\/(?:[a-z0-9-]+\.)?raspberrypi\.org$/, /https:\/\/.+\.editor-standalone-eyq\.pages\.dev$/ ``` Associated updates in Terraform allow for the origins to match the spec in the [issue](#405) (eg. the addition of wildcards / regex matching): * RaspberryPiFoundation/terraform#898
- Loading branch information
Showing
7 changed files
with
92 additions
and
23 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,27 @@ | ||
# frozen_string_literal: true | ||
|
||
# Be sure to restart your server when you modify this file. | ||
# Read more: https://github.com/cyu/rack-cors | ||
|
||
origins_array = ENV['ALLOWED_ORIGINS']&.split(',')&.map(&:strip) || [] | ||
# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin Ajax requests. | ||
require Rails.root.join('lib/origin_parser') | ||
|
||
Rails.application.config.middleware.insert_before 0, Rack::Cors do | ||
allow do | ||
origins origins_array | ||
resource '*', headers: :any, methods: %i[get post patch put delete], expose: ['Link'] | ||
# localhost and test domain origins | ||
origins(%r{https?://localhost([:0-9]*)$}) if Rails.env.development? || Rails.env.test? | ||
|
||
standard_cors_options | ||
end | ||
|
||
allow do | ||
# environment-specific origins set through ALLOWED_ORIGINS env var | ||
# should only be necessary for staging / production environments (see above for local and test) | ||
origins OriginParser.parse_origins | ||
|
||
standard_cors_options | ||
end | ||
end | ||
# Read more: https://github.com/cyu/rack-cors | ||
|
||
# Rails.application.config.middleware.insert_before 0, Rack::Cors do | ||
# allow do | ||
# origins "example.com" | ||
# | ||
# resource "*", | ||
# headers: :any, | ||
# methods: [:get, :post, :put, :patch, :delete, :options, :head] | ||
# end | ||
# end | ||
def standard_cors_options | ||
resource '*', headers: :any, methods: %i[get post patch put delete], expose: ['Link'] | ||
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
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,17 @@ | ||
# frozen_string_literal: true | ||
|
||
# fetch origins from the environment in a comma-separated string | ||
# these can be literal strings or regexes | ||
# regexes must be wrapped in forward slashes eg. /https?:\/\/localhost(:[0-9]*)?$/ | ||
module OriginParser | ||
def self.parse_origins | ||
ENV['ALLOWED_ORIGINS']&.split(',')&.map do |origin| | ||
stripped_origin = origin.strip | ||
if stripped_origin.start_with?('/') && stripped_origin.end_with?('/') | ||
Regexp.new(stripped_origin[1..-2]) | ||
else | ||
stripped_origin | ||
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
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,39 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'rails_helper' | ||
|
||
RSpec.describe OriginParser do | ||
describe '.parse_origins' do | ||
after { ENV['ALLOWED_ORIGINS'] = nil } | ||
|
||
it 'returns an empty array if ALLOWED_ORIGINS is not set' do | ||
ENV['ALLOWED_ORIGINS'] = nil | ||
expect(described_class.parse_origins).to eq([]) | ||
end | ||
|
||
it 'parses literal strings correctly' do | ||
ENV['ALLOWED_ORIGINS'] = 'http://example.com, https://example.org' | ||
expect(described_class.parse_origins).to eq(['http://example.com', 'https://example.org']) | ||
end | ||
|
||
it 'parses regexes correctly' do | ||
ENV['ALLOWED_ORIGINS'] = '/https?:\/\/example\.com/' | ||
expect(described_class.parse_origins).to eq([Regexp.new('https?:\/\/example\.com')]) | ||
end | ||
|
||
it 'parses a mix of literals and regexes' do | ||
ENV['ALLOWED_ORIGINS'] = 'http://example.com, /https?:\/\/localhost$/' | ||
expect(described_class.parse_origins).to eq(['http://example.com', Regexp.new('https?:\/\/localhost$')]) | ||
end | ||
|
||
it 'strips whitespace from origins' do | ||
ENV['ALLOWED_ORIGINS'] = ' http://example.com , /regex$/ ' | ||
expect(described_class.parse_origins).to eq(['http://example.com', Regexp.new('regex$')]) | ||
end | ||
|
||
it 'returns an empty array if ALLOWED_ORIGINS is empty' do | ||
ENV['ALLOWED_ORIGINS'] = '' | ||
expect(described_class.parse_origins).to eq([]) | ||
end | ||
end | ||
end |