Skip to content

Commit

Permalink
[CIOPS-1084] Bearer Token Support (#3)
Browse files Browse the repository at this point in the history
### Description

This pull request adds support for Basic Auth on the endpoint. This
allows projects implementing this gem to secure their endpoint when they
are public facing. That way the Rails stack is safe from being hammered
with requests and prevents the application from being taken down.

### Changes

* Moved the actual endpoint to a dedicated controller
* Added support for Bearer tokens
* Updated the documentation
* Added minitest for the token functionality

### Ticket

[CIOPS-1084](https://customink.atlassian.net/browse/CIOPS-1084)
  • Loading branch information
Arne De Herdt committed Sep 16, 2022
2 parents 1d5ad36 + 463c6d1 commit 9dbf155
Show file tree
Hide file tree
Showing 11 changed files with 103 additions and 13 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Is It Ready? CHANGELOG

## 0.0.4
* Add support for HTTP Authorization Bearer tokens

## 0.0.3
* Make the mounting of the engine dynamic based upon the configuration

Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ or conflicts with another plugin. In this case, creating an initializer under `c
# * https://your-domain/something_else
# This is more for cosmetic purposes, or when mountain multiple engines under the same endpoint with distinct routes.
::IsItReady.endpoint = '/something_else'

# Setting the bearer token will protect the endpoint with Basic HTTP Auth.
# When this value is set, every incoming request on this endpoint must provided the
# AUTHORIZATION header with a Bearer or Token associated to it.
# Failure to do so, will return in an HTTP UNAUTHORIZED response.
::IsItReady.bearer_token = 'my-secret-token'
```

## Contributing
Expand Down
9 changes: 0 additions & 9 deletions app/controllers/is_it_ready/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,5 @@ module IsItReady
# See the general configuration options to enable this protection in the controller.
class ApplicationController < ActionController::Base
protect_from_forgery :with => :exception

# GET /is_it_ready
#
# Returns the desired output, running through the entire Ruby on Rails stack to indicate that
# this application is able to serve requests. The routing is controlled through the Engine,
# but we might be mounted under a specific endpoint or with a custom path.
def is_it_ready
render :json => { :status => 'ok', :code => 200 }
end
end
end
37 changes: 37 additions & 0 deletions app/controllers/is_it_ready/health_check_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# frozen_string_literal: true

module IsItReady
class HealthCheckController < ::IsItReady::ApplicationController
# Disable the CSRF security checks, since this controller will receive calls from external
# services that do not have the ability to generate the required CSRF token
skip_before_action :verify_authenticity_token

# Ensure that all incoming requests supply a valid Bearer token in their Authorization header.
# The function will determine whether the check is required or not based upon the configuration.
before_action :authenticate!

AUTHORIZATION_HEADER = 'HTTP_AUTHORIZATION'

# GET /is_it_ready
#
# Returns the desired output, running through the entire Ruby on Rails stack to indicate that
# this application is able to serve requests. The routing is controlled through the Engine,
# but we might be mounted under a specific endpoint or with a custom path.
def is_it_ready
render :json => { :status => 'ok', :code => 200 }
end

private

# This action will look up the HTTP Authorization header when the configuration has a Bearer token set.
# When the token is set, the incoming requests must provide this as a Bearer token, otherwise the request
# will be refused with an HTTP UNAUTHORIZED response.
def authenticate!
return unless ::IsItReady.bearer_token.present?

authenticate_or_request_with_http_token do |token, _options|
::IsItReady.bearer_token == token
end
end
end
end
2 changes: 1 addition & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
# This follows the same pattern as standard Ruby on Rails routing, but is scoped to
# just the Rails Engine.
::IsItReady::Engine.routes.draw do
root :to => 'application#is_it_ready'
root :to => 'health_check#is_it_ready'
end
6 changes: 6 additions & 0 deletions lib/is_it_ready.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,10 @@ module IsItReady
# but if this is already claimed by an application, it can be overwritten.
mattr_accessor :endpoint
@@endpoint = DEFAULT_PATH

# Sets the Bearer token to be used when securing the endpoint.
# By default this is left blank, and disables the security verification of the controller.
# When enabled however, the request will need to provide the required token as a Bearer value
# in the AUTHORIZATION header of the request.
mattr_accessor :bearer_token
end
2 changes: 1 addition & 1 deletion lib/is_it_ready/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module IsItReady
VERSION = '0.0.3'
VERSION = '0.0.4'
end
2 changes: 1 addition & 1 deletion test/dummy/config/initializers/secret_token.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
# If you change this key, all old signed cookies will become invalid!
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
Dummy::Application.config.secret_token = 'f72b0ec7b89beb76a5a4782594beaa709ced5f43d2a078c71cd4ce26ed2775dffc0e65babff2783e0073be64dabff10ee03e9eca67d7b6d2787111da8ed615a5'
Dummy::Application.config.secret_key_base = 'f72b0ec7b89beb76a5a4782594beaa709ced5f43d2a078c71cd4ce26ed2775dffc0e65babff2783e0073be64dabff10ee03e9eca67d7b6d2787111da8ed615a5'
11 changes: 11 additions & 0 deletions test/dummy/config/initializers/sqlite3.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

# This initializer configures SQLite to silence warnings and make sure the "database" is usable
# across all Rails versions the way a normal database is used during testing.

# Enforce booleans to represented as integers in the database.
# This was an old SQLite feature that we do not want to support anymore.
# Note: This is only for older versions of Sqlite3, the configuration option was dropped at some point
if ::Rails.application.config.active_record.sqlite3.respond_to?(:represent_boolean_as_integer)
::Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true
end
36 changes: 36 additions & 0 deletions test/integration/navigation_with_token_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
require 'test_helper'
require 'securerandom'

module IsItReady
class NavigationWithTokenTest < ActionDispatch::IntegrationTest
include Engine.routes.url_helpers

setup do
::IsItReady.bearer_token = ::SecureRandom.hex(15)
end

teardown do
::IsItReady.bearer_token = nil
end

test('it returns the correct response status on the root') do
get root_url, headers: { 'HTTP_AUTHORIZATION' => "Bearer token=#{::IsItReady.bearer_token}" }

assert_response :success
end

test('it returns the correct output on the root') do
get root_url, headers: { 'HTTP_AUTHORIZATION' => "Bearer token=#{::IsItReady.bearer_token}" }

response = ::JSON.parse(@response.body, symbolize_names: true)

assert_equal({ :status => "ok", :code => 200 }, response)
end

test('it returns the correct response status on the root without token') do
get root_url

assert_response :unauthorized
end
end
end
2 changes: 1 addition & 1 deletion test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
ENV["RAILS_ENV"] = "test"

require File.expand_path("../dummy/config/environment.rb", __FILE__)
require "rails/test_help"
require "minitest/autorun"

Rails.backtrace_cleaner.remove_silencers!

Expand Down

0 comments on commit 9dbf155

Please sign in to comment.