diff --git a/README.md b/README.md index 2d01ab8..5747f66 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,14 @@ If you are using Sprockets, add the ActiveInsights css file to manifest.js: //= link active_insights/application.css ``` +To use HTTP basic authentication, enable it and define a user and password: + +```ruby +config.active_insights.http_basic_auth_enabled = true +config.active_insights.http_basic_auth_user = ENV["BASIC_AUTH_USER"] +config.active_insights.http_basic_auth_password = ENV["BASIC_AUTH_PASSWORD"] +``` + ## Development After checking out the repo, run `bin/setup` to install dependencies. Then, run diff --git a/app/controllers/active_insights/application_controller.rb b/app/controllers/active_insights/application_controller.rb index 6a9bacb..ed8115b 100644 --- a/app/controllers/active_insights/application_controller.rb +++ b/app/controllers/active_insights/application_controller.rb @@ -2,6 +2,8 @@ module ActiveInsights class ApplicationController < ActionController::Base + include ActiveInsights::BasicAuthentication + protect_from_forgery with: :exception around_action :setup_time_zone diff --git a/app/controllers/concerns/active_insights/basic_authentication.rb b/app/controllers/concerns/active_insights/basic_authentication.rb new file mode 100644 index 0000000..bf9e573 --- /dev/null +++ b/app/controllers/concerns/active_insights/basic_authentication.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module ActiveInsights::BasicAuthentication + extend ActiveSupport::Concern + + included do + before_action :authenticate_by_http_basic + end + + def authenticate_by_http_basic + return unless http_basic_authentication_enabled? + + if http_basic_authentication_configured? + http_basic_authenticate_or_request_with(**http_basic_auth_credentials) + else + head :unauthorized + end + end + + def http_basic_authentication_enabled? + ActiveInsights.http_basic_auth_enabled + end + + def http_basic_authentication_configured? + http_basic_auth_credentials.values.all?(&:present?) + end + + def http_basic_auth_credentials + { + name: ActiveInsights.http_basic_auth_user, + password: ActiveInsights.http_basic_auth_password + }.transform_values(&:presence) + end +end diff --git a/lib/active_insights.rb b/lib/active_insights.rb index 328912a..c93e34b 100644 --- a/lib/active_insights.rb +++ b/lib/active_insights.rb @@ -7,7 +7,9 @@ require "active_insights/engine" module ActiveInsights - mattr_accessor :connects_to, :ignored_endpoints, :enabled + mattr_accessor :connects_to, :ignored_endpoints, :enabled, + :http_basic_auth_enabled, :http_basic_auth_user, + :http_basic_auth_password class << self def ignored_endpoint?(payload) diff --git a/lib/active_insights/version.rb b/lib/active_insights/version.rb index 5c37645..419406e 100644 --- a/lib/active_insights/version.rb +++ b/lib/active_insights/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module ActiveInsights - VERSION = "1.3.1" + VERSION = "1.4.0" end diff --git a/test/active_insights_test.rb b/test/active_insights_test.rb index 46e96a7..b24f78b 100644 --- a/test/active_insights_test.rb +++ b/test/active_insights_test.rb @@ -3,7 +3,35 @@ require "test_helper" class ActiveInsightsTest < ActiveSupport::TestCase + teardown do + ActiveInsights.http_basic_auth_enabled = nil + ActiveInsights.http_basic_auth_user = nil + ActiveInsights.http_basic_auth_password = nil + end + test "it has a version number" do assert ActiveInsights::VERSION end + + test "it does not enabled http basic auth by default" do + assert_not ActiveInsights.http_basic_auth_enabled + end + + test "it has an accessor to set whether http basic auth is enabled" do + ActiveInsights.http_basic_auth_enabled = true + + assert ActiveInsights.http_basic_auth_enabled + end + + test "it has an accessor to set http basic auth user" do + ActiveInsights.http_basic_auth_user = "johndoe" + + assert_equal "johndoe", ActiveInsights.http_basic_auth_user + end + + test "it has an accessor to set http basic auth password" do + ActiveInsights.http_basic_auth_password = "secret" + + assert_equal "secret", ActiveInsights.http_basic_auth_password + end end diff --git a/test/controllers/concerns/active_insights/basic_authentication_test.rb b/test/controllers/concerns/active_insights/basic_authentication_test.rb new file mode 100644 index 0000000..5420e41 --- /dev/null +++ b/test/controllers/concerns/active_insights/basic_authentication_test.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +require "test_helper" + +class ActiveInsights::BasicAuthenticationTest < ActionDispatch::IntegrationTest + test "it does not require http basic auth by default" do + get active_insights.requests_path + + assert_response :success + end + + test "it requires http basic auth when enabled" do + with_http_basic_auth do + get active_insights.requests_path + + assert_response :unauthorized + end + end + + test "allows access with correct credentials" do + with_http_basic_auth(user: "dev", password: "secret") do + get active_insights.requests_path, headers: auth_headers("dev", "secret") + + assert_response :success + end + end + + test "disallows access with incorrect credentials" do + with_http_basic_auth(user: "dev", password: "secret") do + get active_insights.requests_path, headers: auth_headers("dev", "wrong") + + assert_response :unauthorized + end + end + + private + + # rubocop:disable Metrics/MethodLength + def with_http_basic_auth(enabled: true, user: nil, password: nil) + previous_enabled, ActiveInsights.http_basic_auth_enabled = + ActiveInsights.http_basic_auth_enabled, enabled + previous_user, ActiveInsights.http_basic_auth_user = + ActiveInsights.http_basic_auth_user, user + previous_password, ActiveInsights.http_basic_auth_password = + ActiveInsights.http_basic_auth_password, password + yield + ensure + ActiveInsights.http_basic_auth_enabled = previous_enabled + ActiveInsights.http_basic_auth_user = previous_user + ActiveInsights.http_basic_auth_password = previous_password + end + # rubocop:enable Metrics/MethodLength + + def auth_headers(user, password) + { + Authorization: + ActionController::HttpAuthentication::Basic.encode_credentials( + user, password + ) + } + end +end