Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
class ApplicationController < ActionController::API
include FacetRailsCommon::ApplicationControllerMethods

# Ensure all API endpoints support CORS when Origin header is present
before_action :set_cors_headers

private

def set_cors_headers
if request.headers['Origin']
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD'
response.headers['Access-Control-Allow-Headers'] = 'Origin, Content-Type, Accept, Authorization, X-Requested-With'
response.headers['Cross-Origin-Resource-Policy'] = 'cross-origin'
end
end
end
52 changes: 52 additions & 0 deletions app/controllers/ethscriptions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,27 @@ def data
blockhash, block_number = scope.pick(:block_blockhash, :block_number)

unless blockhash.present?
# Ensure CORS headers are set even for 404 responses
if request.headers['Origin']
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Origin, Content-Type, Accept, Authorization'
response.headers['Cross-Origin-Resource-Policy'] = 'cross-origin'
end
head 404
return
end

response.headers.delete('X-Frame-Options')

# Ensure CORS headers are set for cross-origin requests
if request.headers['Origin']
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Origin, Content-Type, Accept, Authorization'
response.headers['Cross-Origin-Resource-Policy'] = 'cross-origin'
end

set_cache_control_headers(
max_age: 6,
s_max_age: 1.minute,
Expand All @@ -140,6 +155,17 @@ def data
end
end

def data_options
# Handle CORS preflight requests for the data endpoint
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Origin, Content-Type, Accept, Authorization'
response.headers['Access-Control-Max-Age'] = '3600'
response.headers['Cross-Origin-Resource-Policy'] = 'cross-origin'

head :ok
end

def attachment
scope = Ethscription.all

Expand All @@ -154,12 +180,27 @@ def attachment
attachment_scope = EthscriptionAttachment.where(sha: sha)

unless attachment_scope.exists?
# Ensure CORS headers are set even for 404 responses
if request.headers['Origin']
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Origin, Content-Type, Accept, Authorization'
response.headers['Cross-Origin-Resource-Policy'] = 'cross-origin'
end
head 404
return
end

response.headers.delete('X-Frame-Options')

# Ensure CORS headers are set for cross-origin requests
if request.headers['Origin']
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Origin, Content-Type, Accept, Authorization'
response.headers['Cross-Origin-Resource-Policy'] = 'cross-origin'
end

set_cache_control_headers(
max_age: 6,
s_max_age: 1.minute,
Expand All @@ -172,6 +213,17 @@ def attachment
end
end

def attachment_options
# Handle CORS preflight requests for the attachment endpoint
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Origin, Content-Type, Accept, Authorization'
response.headers['Access-Control-Max-Age'] = '3600'
response.headers['Cross-Origin-Resource-Policy'] = 'cross-origin'

head :ok
end

def exists
existing = Ethscription.find_by_content_sha(params[:sha])

Expand Down
2 changes: 2 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ def draw_routes
resources :ethscriptions, only: [:index, :show] do
collection do
get "/:id/data", to: "ethscriptions#data"
match "/:id/data", to: "ethscriptions#data_options", via: :options
# get "/newer_ethscriptions", to: "ethscriptions#newer_ethscriptions"
# get "/newer", to: "ethscriptions#newer_ethscriptions"
get '/owned_by/:owned_by_address', to: 'ethscriptions#index'
Expand All @@ -12,6 +13,7 @@ def draw_routes

member do
get 'attachment', to: 'ethscriptions#attachment'
match 'attachment', to: 'ethscriptions#attachment_options', via: :options
end
end

Expand Down
100 changes: 100 additions & 0 deletions spec/requests/cors_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
require 'rails_helper'

RSpec.describe 'CORS for Data and Attachment Endpoints', type: :request do
describe 'Ethscriptions data endpoint CORS handling' do
context 'OPTIONS preflight request' do
it 'responds with appropriate CORS headers for preflight request' do
options "/ethscriptions/1/data",
headers: {
'Origin' => 'https://example.com',
'Access-Control-Request-Method' => 'GET',
'Access-Control-Request-Headers' => 'content-type'
}

expect(response.status).to eq(200)
expect(response.headers['Access-Control-Allow-Origin']).to eq('*')
expect(response.headers['Access-Control-Allow-Methods']).to eq('GET, OPTIONS')
expect(response.headers['Access-Control-Allow-Headers']).to eq('Origin, Content-Type, Accept, Authorization')
expect(response.headers['Access-Control-Max-Age']).to eq('3600')
expect(response.headers['Cross-Origin-Resource-Policy']).to eq('cross-origin')
end
end

context 'GET request with Origin header' do
it 'includes CORS headers when Origin header is present' do
# Note: This test may fail if ethscription #1 doesn't exist, but the CORS headers should still be set
get "/ethscriptions/1/data",
headers: { 'Origin' => 'https://example.com' }

# Check CORS headers are present regardless of whether the ethscription exists
expect(response.headers['Access-Control-Allow-Origin']).to eq('*')
expect(response.headers['Access-Control-Allow-Methods']).to eq('GET, OPTIONS')
expect(response.headers['Access-Control-Allow-Headers']).to eq('Origin, Content-Type, Accept, Authorization')
expect(response.headers['Cross-Origin-Resource-Policy']).to eq('cross-origin')
end

it 'does not include CORS headers when Origin header is not present' do
get "/ethscriptions/1/data"

# CORS headers should not be set when no Origin header is present
expect(response.headers['Access-Control-Allow-Origin']).to be_nil
end
end
end

describe 'Ethscriptions attachment endpoint CORS handling' do
context 'OPTIONS preflight request' do
it 'responds with appropriate CORS headers for preflight request' do
options "/ethscriptions/1/attachment",
headers: {
'Origin' => 'https://example.com',
'Access-Control-Request-Method' => 'GET',
'Access-Control-Request-Headers' => 'content-type'
}

expect(response.status).to eq(200)
expect(response.headers['Access-Control-Allow-Origin']).to eq('*')
expect(response.headers['Access-Control-Allow-Methods']).to eq('GET, OPTIONS')
expect(response.headers['Access-Control-Allow-Headers']).to eq('Origin, Content-Type, Accept, Authorization')
expect(response.headers['Access-Control-Max-Age']).to eq('3600')
expect(response.headers['Cross-Origin-Resource-Policy']).to eq('cross-origin')
end
end

context 'GET request with Origin header' do
it 'includes CORS headers when Origin header is present' do
# Note: This test may fail if ethscription #1 doesn't exist, but the CORS headers should still be set
get "/ethscriptions/1/attachment",
headers: { 'Origin' => 'https://example.com' }

# Check CORS headers are present regardless of whether the ethscription exists
expect(response.headers['Access-Control-Allow-Origin']).to eq('*')
expect(response.headers['Access-Control-Allow-Methods']).to eq('GET, OPTIONS')
expect(response.headers['Access-Control-Allow-Headers']).to eq('Origin, Content-Type, Accept, Authorization')
expect(response.headers['Cross-Origin-Resource-Policy']).to eq('cross-origin')
end
end
end

describe 'Global CORS handling for other endpoints' do
context 'GET request to index endpoint with Origin header' do
it 'includes global CORS headers when Origin header is present' do
get "/ethscriptions",
headers: { 'Origin' => 'https://example.com' }

# Check global CORS headers are present
expect(response.headers['Access-Control-Allow-Origin']).to eq('*')
expect(response.headers['Access-Control-Allow-Methods']).to eq('GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD')
expect(response.headers['Access-Control-Allow-Headers']).to eq('Origin, Content-Type, Accept, Authorization, X-Requested-With')
expect(response.headers['Cross-Origin-Resource-Policy']).to eq('cross-origin')
end

it 'does not include CORS headers when Origin header is not present' do
get "/ethscriptions"

# CORS headers should not be set when no Origin header is present
expect(response.headers['Access-Control-Allow-Origin']).to be_nil
end
end
end
end