diff --git a/.codeclimate.yml b/.codeclimate.yml index 557e5b2f..0365572d 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -19,4 +19,4 @@ exclude_patterns: - vendor/ - "**/vendor/**/*" - app/assets/images/ -- spec/**/* \ No newline at end of file +- spec/**/* diff --git a/.env_login b/.env_dev similarity index 58% rename from .env_login rename to .env_dev index 56b421dd..48146744 100644 --- a/.env_login +++ b/.env_dev @@ -6,3 +6,13 @@ export LOGOUT_REDIRECT_EVAL_URL=http://localhost:3000/ export PHOENIX_URI="http://localhost:4000" export LOGIN_SECRET="f4d3c40a00a8e6ed72fae5204d9ddacd40f087865d40803a6fcfb935591a271838533f06081067dac24c0085c74123e7e1c8b3e0ab562c6645b17eb769854d0d" export JWT_SECRET="fc28c5738ca45162f61126e770a8fbdbd938d0fedcfe8fbb9f851b855b0264866364a9130e96aca8b1977e9f58edf064f1aa435ceccf415ff22fd3c24adba320" + +# static pages on localhost +# export PAGES_DOMAIN="localhost:4001" +# export PAGES_HOST="http://localhost:4001" +# export PAGES_BASE_URL="/" + +# static pages on eval-dev branch +export PAGES_DOMAIN="federalist-2c628203-05c2-48ab-8f87-3eda79380559.sites.pages.cloud.gov" +export PAGES_HOST="https://federalist-2c628203-05c2-48ab-8f87-3eda79380559.sites.pages.cloud.gov" +export PAGES_BASE_URL="/preview/gsa/challenges-and-prizes/eval-dev" \ No newline at end of file diff --git a/.envrc b/.envrc index 4fbb075a..ebe455a6 100644 --- a/.envrc +++ b/.envrc @@ -6,4 +6,4 @@ mkdir -p .nix-bundler export BUNDLE_PATH=./.nix-bundler # Login Env Vars -source .env_login +source .env_dev diff --git a/.simplecov b/.simplecov index 90a22a90..1d3e67c4 100644 --- a/.simplecov +++ b/.simplecov @@ -28,6 +28,7 @@ SimpleCov.start 'rails' do add_filter '/app/jobs/application_job.rb' add_filter '/app/mailers/application_mailer.rb' add_filter '/app/models/application_record.rb' + add_filter '/app/controllers/pages_controller.rb' add_filter '/app/controllers/sandbox_controller.rb' diff --git a/DEVCONFIG.md b/DEVCONFIG.md index c9b2a296..9e03e755 100644 --- a/DEVCONFIG.md +++ b/DEVCONFIG.md @@ -66,7 +66,7 @@ Once direnv is installed and your shell is restarted, clone the project and `cd` ``` ./bin/dev ``` - > _NOTE for login.gov configuration_ -- if you are **not** using direnv/nix to eval `.envrc`, you can run `source .env_login` in your terminal before starting the server or add the env vars in that file to your local environment directly. + > _NOTE for login.gov configuration_ -- if you are **not** using direnv/nix to eval `.envrc`, you can run `source .env_dev` in your terminal before starting the server or add the env vars in that file to your local environment directly. Now you can visit [`localhost:3000`](http://localhost:3000) from your browser. diff --git a/Gemfile b/Gemfile index 1dcb097f..40da774e 100644 --- a/Gemfile +++ b/Gemfile @@ -102,3 +102,5 @@ end gem "factory_bot", "~> 6.5" gem "faker", "~> 3.4" + +gem "rails-reverse-proxy" diff --git a/Gemfile.lock b/Gemfile.lock index 66543f85..5a39e28e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -255,6 +255,9 @@ GEM rails-html-sanitizer (1.6.0) loofah (~> 2.21) nokogiri (~> 1.14) + rails-reverse-proxy (0.13.0) + actionpack + addressable railties (7.2.2) actionpack (= 7.2.2) activesupport (= 7.2.2) @@ -398,6 +401,7 @@ DEPENDENCIES puma (>= 6.4.3) rails (~> 7.2.1) rails-controller-testing + rails-reverse-proxy rspec-rails rspec_junit_formatter rubocop (>= 1.66.0) diff --git a/app/controllers/pages_controller.rb b/app/controllers/pages_controller.rb new file mode 100644 index 00000000..79cb99e9 --- /dev/null +++ b/app/controllers/pages_controller.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +# Proxy Cloud.gov pages content at the root of the application +class PagesController < ApplicationController + include ReverseProxy::Controller + # We must remove this for proxy of JS assets to be loaded by the browser + protect_from_forgery except: :assets + + # TODO: When launched, the cloud.gov pages need to move off the www.challenge.gov domain + DOMAIN = Rails.configuration.static_site_interop.fetch(:domain) + HOST = Rails.configuration.static_site_interop.fetch(:host) + BASE_URL = Rails.configuration.static_site_interop.fetch(:base_url) + + def index + path = "#{BASE_URL}/#{params[:path]}/" + reverse_proxy(HOST, path:, reset_accept_encoding: true, headers: { host: DOMAIN }) do |config| + config.on_missing do |_code, _response| + redirect_to "/dashboard" + return true + end + + config.on_response do |_code, response| + response.body = rewrite_links(response.body) + end + end + end + + def assets + if params[:ext] == "min" + path = "#{HOST}#{BASE_URL}/assets/#{params[:path]}.#{params[:ext]}.js" + response = Faraday.get(path) + send_data(response.body, type: 'application/javascript') + else + path = "#{BASE_URL}/assets/#{params[:path]}.#{params[:ext]}" + reverse_proxy(HOST, path:, reset_accept_encoding: true, headers: { host: DOMAIN }) + end + end + + def root + path = "#{BASE_URL}/" + reverse_proxy(HOST, path:, reset_accept_encoding: true, headers: { host: DOMAIN }) do |config| + config.on_response do |_code, response| + if response.body.present? + response.body = rewrite_links(response.body) + end + end + end + end + + private + + def rewrite_links(html) + parsed_html = html.gsub(HOST, "/") + if BASE_URL.length > 1 + parsed_html = parsed_html.gsub(BASE_URL, "") + end + # delete the data-public-url attribute from the react app element to send requests through rails proxy + parsed_html = parsed_html.sub(/(
"rails/health#show", as: :rails_health_check - # Defines the root path route ("/") - # root "posts#index" if Rails.env.development? || Rails.env.dev? || Rails.env.test? namespace :dev do get "/sandbox", to: "sandbox#index" @@ -44,4 +41,8 @@ post "/login", to: "accounts#login" end end + + match '/assets/*path.:ext' => 'pages#assets', via: [:get] + match '/*path' => 'pages#index', via: [:get] + match '/' => 'pages#root', via: [:get] end diff --git a/spec/requests/dashboard_request_spec.rb b/spec/requests/dashboard_request_spec.rb index 17844922..0e954144 100644 --- a/spec/requests/dashboard_request_spec.rb +++ b/spec/requests/dashboard_request_spec.rb @@ -3,61 +3,6 @@ RSpec.describe "DashboardController" do let(:user) { create_and_log_in_user } - describe "GET /" do - before { get "/" } - - it_behaves_like "a page with footer content" - it_behaves_like "a page with header content" - - context "when logged in as super admin on the root url" do - before do - user.update(role: "super_admin") - get "/" - end - - it_behaves_like "a page with dashboard content for a super admin" - end - - context "when logged in as admin on the root url" do - before do - user.update(role: "admin") - get "/" - end - - it_behaves_like "a page with dashboard content for an admin" - end - - context "when logged in as public solver on the root url" do - before do - user.update(role: "solver") - get "/" - end - - it_behaves_like "a page with dashboard content for a public solver" - end - - context "when logged in as a challenge manager on the root url" do - before do - user.update(role: "challenge_manager") - get "/" - end - - it_behaves_like "a page with utility menu links for all users" - it_behaves_like "a page with utility menu links for a challenge manager" - it_behaves_like "a page with dashboard content for a challenge manager" - end - - context "when logged in as an evaluator on the root url" do - before do - user.update(role: "evaluator") - get "/" - end - - it_behaves_like "a page with utility menu links for all users" - it_behaves_like "a page with dashboard content for an evaluator" - end - end - describe "GET /dashboard" do before { get "/dashboard" } diff --git a/spec/requests/pages_spec.rb b/spec/requests/pages_spec.rb new file mode 100644 index 00000000..a8952447 --- /dev/null +++ b/spec/requests/pages_spec.rb @@ -0,0 +1,43 @@ +require "rails_helper" + +RSpec.describe "PagesController" do + it "get root renders successfully" do + stub_request(:get, PagesController::HOST + PagesController::BASE_URL + "/"). + to_return(status: 200, body: "", headers: {}) + + get "/" + expect(response).to be_ok + end + + it "removes full paths to assets so they proxy too" do + stub_request(:get, PagesController::HOST + PagesController::BASE_URL + "/"). + to_return(status: 200, body: PagesController::HOST + PagesController::BASE_URL, headers: {}) + + get "/" + expect(response.body).to eq("/") + end + + it "works for minified js assets" do + stub_request(:get, PagesController::HOST + PagesController::BASE_URL + "/assets/uswds.min.js"). + to_return(status: 200, body: "", headers: {}) + + get "/assets/uswds.min.js" + expect(response).to be_ok + end + + it "works for image assets" do + stub_request(:get, PagesController::HOST + PagesController::BASE_URL + "/assets/logo.svg"). + to_return(status: 200, body: "", headers: {}) + + get "/assets/logo.svg" + expect(response).to be_ok + end + + it "404s to the dashboard" do + stub_request(:get, PagesController::HOST + PagesController::BASE_URL + "/not_found/"). + to_return(status: 404, body: "", headers: {}) + + get "/not_found" + expect(response).to redirect_to("/dashboard") + end +end diff --git a/spec/system/logins_spec.rb b/spec/system/logins_spec.rb index cf70fa3a..54655141 100644 --- a/spec/system/logins_spec.rb +++ b/spec/system/logins_spec.rb @@ -10,7 +10,8 @@ end describe "Logged-out" do - it "web root page is accessible" do + xit "web root page is accessible" do + # Marking as pending as the root page is proxied pages content for now visit "/" expect(page).to(be_axe_clean) end