From c8f23bd2edc19f968446b149120df1f7798eb4b1 Mon Sep 17 00:00:00 2001
From: DJ Mountney <david@twkie.net>
Date: Wed, 11 May 2016 17:27:08 -0700
Subject: [PATCH] Support token header for health check token, and general
 cleanup of the health_check feature.

---
 app/controllers/health_check_controller.rb    | 15 ++++++--
 app/views/admin/health_check/show.html.haml   | 36 +++++++++++--------
 config/initializers/health_check.rb           |  2 +-
 config/routes.rb                              |  2 +-
 .../health_check_controller_spec.rb           | 15 ++++++++
 5 files changed, 50 insertions(+), 20 deletions(-)

diff --git a/app/controllers/health_check_controller.rb b/app/controllers/health_check_controller.rb
index b974489836f..037da7d2bce 100644
--- a/app/controllers/health_check_controller.rb
+++ b/app/controllers/health_check_controller.rb
@@ -1,13 +1,22 @@
 class HealthCheckController < HealthCheck::HealthCheckController
   before_action :validate_health_check_access!
 
-  protected
+  private
 
   def validate_health_check_access!
-    return render_404 unless params[:token].presence && params[:token] == current_application_settings.health_check_access_token
+    render_404 unless token_valid?
+  end
+
+  def token_valid?
+    token = params[:token].presence || request.headers['TOKEN']
+    token.present? &&
+      ActiveSupport::SecurityUtils.variable_size_secure_compare(
+        token,
+        current_application_settings.health_check_access_token
+      )
   end
 
   def render_404
-    render file: Rails.root.join("public", "404"), layout: false, status: "404"
+    render file: Rails.root.join('public', '404'), layout: false, status: '404'
   end
 end
diff --git a/app/views/admin/health_check/show.html.haml b/app/views/admin/health_check/show.html.haml
index 23a931995ab..ed7025f7a0b 100644
--- a/app/views/admin/health_check/show.html.haml
+++ b/app/views/admin/health_check/show.html.haml
@@ -2,29 +2,35 @@
 
 %h3.page-title
   Health Check
-%p.light
+.bs-callout.clearfix
+  .pull-left
+    %p
   Access token is
-  %code{ id:'health-check-token' }= "#{current_application_settings.health_check_access_token}"
+  %code#health-check-token= current_application_settings.health_check_access_token
+  = button_to reset_health_check_token_admin_application_settings_path,
+    method: :put, class: 'btn btn-default',
+    data: { confirm: 'Are you sure you want to reset the health check token?' } do
+    = icon('refresh')
+    Reset health check access token
 %p.light
   Health information can be reteived as plain text, json, or xml using:
   %ul
     %li
-      %code= "/health_check?token=#{current_application_settings.health_check_access_token}"
+      %code= health_check_url(token:current_application_settings.health_check_access_token)
     %li
-      %code= "/health_check.json?token=#{current_application_settings.health_check_access_token}"
+      %code= health_check_url(token:current_application_settings.health_check_access_token, format: :json)
     %li
-      %code= "/health_check.xml?token=#{current_application_settings.health_check_access_token}"
+      %code= health_check_url(token:current_application_settings.health_check_access_token, format: :xml)
 
-.bs-callout.clearfix
-  .pull-left
-    %p
-      You can reset the health check access token by pressing the button below.
-    %p
-      = button_to reset_health_check_token_admin_application_settings_path,
-        method: :put, class: 'btn btn-default',
-        data: { confirm: 'Are you sure you want to reset the health check token?' } do
-        = icon('refresh')
-        Reset health check access token
+%p.light
+  You can also ask for the status of specific services:
+  %ul
+    %li
+      %code= health_check_url(token:current_application_settings.health_check_access_token, checks: :cache)
+    %li
+      %code= health_check_url(token:current_application_settings.health_check_access_token, checks: :database)
+    %li
+      %code= health_check_url(token:current_application_settings.health_check_access_token, checks: :migrations)
 
 %hr
 .panel.panel-default
diff --git a/config/initializers/health_check.rb b/config/initializers/health_check.rb
index a93e4273030..79e2d23ab2e 100644
--- a/config/initializers/health_check.rb
+++ b/config/initializers/health_check.rb
@@ -1,3 +1,3 @@
 HealthCheck.setup do |config|
-  config.standard_checks = [ 'database', 'migrations', 'cache' ]
+  config.standard_checks = ['database', 'migrations', 'cache']
 end
diff --git a/config/routes.rb b/config/routes.rb
index c81bf294a53..f794a881f71 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -74,7 +74,7 @@ Rails.application.routes.draw do
   end
 
   # Health check
-  get 'health_check(/:checks)(.:format)'  => 'health_check#index'
+  get 'health_check(/:checks)'  => 'health_check#index', as: :health_check
 
   # Enable Grack support
   mount Grack::AuthSpawner, at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\//.match(request.path_info) }, via: [:get, :post, :put]
diff --git a/spec/controllers/health_check_controller_spec.rb b/spec/controllers/health_check_controller_spec.rb
index 3b9cc5c98f5..0d8a68bb51a 100644
--- a/spec/controllers/health_check_controller_spec.rb
+++ b/spec/controllers/health_check_controller_spec.rb
@@ -14,6 +14,13 @@ describe HealthCheckController do
     end
 
     context 'when services are up and an access token is provided' do
+      it 'supports passing the token in the header' do
+        request.headers['TOKEN'] = token
+        get :index
+        expect(response).to be_success
+        expect(response.content_type).to eq 'text/plain'
+      end
+
       it 'supports successful plaintest response' do
         get :index, token: token
         expect(response).to be_success
@@ -55,6 +62,14 @@ describe HealthCheckController do
         allow(HealthCheck::Utils).to receive(:process_checks).with('email').and_return('Email is on fire')
       end
 
+      it 'supports passing the token in the header' do
+        request.headers['TOKEN'] = token
+        get :index
+        expect(response.status).to eq(500)
+        expect(response.content_type).to eq 'text/plain'
+        expect(response.body).to include('The server is on fire')
+      end
+
       it 'supports failure plaintest response' do
         get :index, token: token
         expect(response.status).to eq(500)
-- 
2.30.9