Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade defaults ror 7.1 #7292

Merged
merged 8 commits into from
Nov 25, 2024
Merged
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
46 changes: 23 additions & 23 deletions app/controllers/omniauth_callbacks_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,29 @@ def unknown_organisation_category
render "unknown_organisation_category", status: :forbidden
end

def not_found(error)
# Overrides `ApplicationController`, which globally rescues all `ActiveRecord::RecordNotFound`
# errors and shows a "page not found" error page to the user.
# It's unexpected for a record to not be found as part of the sign in process, so send this
# error to our error tracking system so it can be investigated.
Sentry.with_scope do |scope|
scope.set_context(
"Authentication Context",
{
user_id: user_id,
auth_hash_info: auth_hash["info"],
auth_hash_extra: auth_hash["extra"],
},
)

Sentry.capture_exception(error)
end

Rails.logger.error("Not found error encountered during sign in", error)

super
end

private

def auth_hash
Expand Down Expand Up @@ -110,29 +133,6 @@ def use_school_group_if_available(publisher, organisation)
session.update(publisher_organisation_id: publisher_organisation.id) if publisher_organisation
end

def not_found(error)
# Overrides `ApplicationController`, which globally rescues all `ActiveRecord::RecordNotFound`
# errors and shows a "page not found" error page to the user.
# It's unexpected for a record to not be found as part of the sign in process, so send this
# error to our error tracking system so it can be investigated.
Sentry.with_scope do |scope|
scope.set_context(
"Authentication Context",
{
user_id: user_id,
auth_hash_info: auth_hash["info"],
auth_hash_extra: auth_hash["extra"],
},
)

Sentry.capture_exception(error)
end

Rails.logger.error("Not found error encountered during sign in", error)

super
end

def report_omniauth_error
omniauth_error = request.respond_to?(:get_header) ? request.get_header("omniauth.error") : request.env["omniauth.error"]

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
class Publishers::CandidateProfilesInterstitialsController < Publishers::BaseController
skip_before_action :check_candidate_profiles_interstitial_acknowledged, only: %i[show]
skip_before_action :check_terms_and_conditions, only: %i[show update]
skip_before_action :check_terms_and_conditions, only: %i[show]
scruti marked this conversation as resolved.
Show resolved Hide resolved

def show
current_publisher.update(acknowledged_candidate_profiles_interstitial: true)
Expand Down
14 changes: 9 additions & 5 deletions app/models/subscription.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,31 @@ class Subscription < ApplicationRecord

validates :email, email_address: true, if: -> { email_changed? } # Allows data created prior to validation to still be valid

def self.encryptor
def self.encryptor(serializer: :json_allow_marshal)
key_generator_secret = SUBSCRIPTION_KEY_GENERATOR_SECRET
key_generator_salt = SUBSCRIPTION_KEY_GENERATOR_SALT

key_generator = ActiveSupport::KeyGenerator
.new(key_generator_secret, hash_digest_class: SUBSCRIPTION_KEY_GENERATOR_DIGEST_CLASS)
.generate_key(key_generator_salt, 32)

ActiveSupport::MessageEncryptor.new(key_generator)
ActiveSupport::MessageEncryptor.new(key_generator, serializer: serializer)
end

def self.find_and_verify_by_token(token)
data = encryptor.decrypt_and_verify(token)
find(data[:id])
data = begin
encryptor(serializer: :json_allow_marshal).decrypt_and_verify(token)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need this change? It looks this would get even more complicated with Rails 7.2. Are there any down-sides to just using the default?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows previous subscriptions encrypted with marshal to be decrypted as we migrate towards json in the next Rails release.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think a comment (as per your slack message) would be useful here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need the decrypt to be json_allow_marhsall to allow previously encrypted subscriptions (with the old marshal serialiser) to be read. If we want to keep json only, we might have to write a data migration to re-encrypt the subscription.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant in the code...

rescue ActiveSupport::MessageEncryptor::InvalidMessage
encryptor(serializer: :marshal).decrypt_and_verify(token)
end
find(data.symbolize_keys[:id])
rescue ActiveSupport::MessageEncryptor::InvalidMessage
raise ActiveRecord::RecordNotFound
end

def token
token_values = { id: id }
self.class.encryptor.encrypt_and_sign(token_values)
self.class.encryptor(serializer: :json_allow_marshal).encrypt_and_sign(token_values)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

having defaulted the encryptor to :json_allow_marshal, we don't need to make this change IMHO

end

def unsubscribe
Expand Down
35 changes: 34 additions & 1 deletion config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@

module TeachingVacancies
class Application < Rails::Application
config.load_defaults 7.0
config.load_defaults 7.1

config.add_autoload_paths_to_load_path = false

config.time_zone = "Europe/London"

Expand All @@ -38,6 +40,17 @@ class Application < Rails::Application
config.action_view.sanitized_allowed_tags = %w[p br strong em ul li h1 h2 h3 h4 h5]
config.action_view.default_form_builder = GOVUKDesignSystemFormBuilder::FormBuilder

# Given we are using Lockbox, this ensures that Rails does not include unnecessary support for SHA-1,
# which is deprecated and considered insecure.
config.active_record.encryption.support_sha1_for_non_deterministic_encryption = false

# Disable deprecated singular associations names.
config.active_record.allow_deprecated_singular_associations_name = false

# No longer run after_commit callbacks on the first of multiple Active Record
# instances to save changes to the same database row within a transaction.
config.active_record.run_commit_callbacks_on_first_saved_instances_in_transaction = false

# Settings in config/environments/* take precedence over those
# specified here.
# Application configuration should go into files in config/initializers
Expand All @@ -58,6 +71,23 @@ class Application < Rails::Application
config.active_storage.routes_prefix = "/attachments"
config.active_storage.resolve_model_to_route = :rails_storage_proxy

# Specify the default serializer used by `MessageEncryptor` and `MessageVerifier`
# instances.
#
# The legacy default is `:marshal`, which is a potential vector for
# deserialization attacks in cases where a message signing secret has been
# leaked.
#
# In Rails 7.1, the new default is `:json_allow_marshal` which serializes and
# deserializes with `ActiveSupport::JSON`, but can fall back to deserializing
# with `Marshal` so that legacy messages can still be read.
#
# In Rails 7.2, the default will become `:json` which serializes and
# deserializes with `ActiveSupport::JSON` only.
config.active_support.message_serializer = :json_allow_marshal

config.active_support.use_message_serializer_for_metadata = true

config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info").to_sym

# Set up backing services through VCAP_SERVICES if running on AKS
Expand Down Expand Up @@ -110,6 +140,9 @@ class Application < Rails::Application
# we figure out a way around that, this keeps the pre-Rails 7 default around.
Rails.application.config.action_controller.raise_on_open_redirects = false

# Do not treat an `ActionController::Parameters` instance as equal to an equivalent `Hash` by default.
Rails.application.config.action_controller.allow_deprecated_parameters_hash_equality = false

Rails.autoloaders.main.ignore(Rails.root.join("app/frontend"))

config.after_initialize do |app|
Expand Down
20 changes: 15 additions & 5 deletions config/environments/development.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
require "active_support/core_ext/integer/time"

Rails.application.configure do
# Settings specified here will take precedence over those in
# config/application.rb.
# Settings specified here will take precedence over those in config/application.rb.

# The application uses multiple services for storing files. This sets up a default value which gets overridden
# in every specific use case.
Expand All @@ -14,10 +13,10 @@
# If developing through Github codespaces: allow the port forwarding domain to access the app.
config.action_controller.forgery_protection_origin_check = false if ENV["GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN"].present?

# In the development environment your application's code is reloaded on
# every request. This slows down response time but is perfect for development
# In the development environment your application's code is reloaded any time
# it changes. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.
config.cache_classes = false
config.enable_reloading = true

# Do not eager load code on boot.
config.eager_load = false
Expand All @@ -39,6 +38,7 @@
BetterErrors::Middleware.allow_ip! "192.168.0.0/16"

# Enable/disable caching. By default caching is disabled.
# Run rails dev:cache to toggle caching.
if Rails.root.join("tmp/caching-dev.txt").exist?
config.action_controller.perform_caching = true
config.action_controller.enable_fragment_cache_logging = true
Expand All @@ -62,6 +62,10 @@
# Tell Active Support which deprecation messages to disallow.
config.active_support.disallowed_deprecation_warnings = []

# Specify if an `ArgumentError` should be raised if `Rails.cache` `fetch` or
# `write` are given an invalid `expires_at` or `expires_in` time.
config.active_support.raise_on_invalid_cache_expiration_time = true

# Raise an error on page load if there are pending migrations.
config.active_record.migration_error = false

Expand All @@ -76,6 +80,9 @@
# Highlight code that triggered database queries in logs.
config.active_record.verbose_query_logs = true

# Highlight code that enqueued background job in logs.
config.active_job.verbose_enqueue_logs = true

# Use an evented file watcher to asynchronously detect changes in source code,
# routes, locales, etc. This feature depends on the listen gem.
config.file_watcher = ActiveSupport::EventedFileUpdateChecker
Expand All @@ -87,6 +94,9 @@
# Annotate rendered view with file names.
config.action_view.annotate_rendered_view_with_filenames = true

# Raise error when a before_action's only/except options reference missing actions
config.action_controller.raise_on_missing_callback_actions = true

config.i18n.raise

config.log_file_size = 100.megabytes
Expand Down
22 changes: 15 additions & 7 deletions config/environments/production.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require "active_support/core_ext/integer/time"

Rails.application.configure do
# Settings specified here will take precedence over those in
# config/application.rb.
# Settings specified here will take precedence over those in config/application.rb.

# The application uses multiple services for storing files. This sets up a default value which gets overridden
# in every specific use case.
Expand All @@ -10,12 +11,11 @@
config.allowed_cors_origin = proc { "https://#{DOMAIN}" }

# Code is not reloaded between requests.
config.cache_classes = true
config.enable_reloading = false

# Eager load code on boot. This eager loads most of Rails and
# your application in memory, allowing both threaded web servers
# and those relying on copy on write to perform better.
# Rake tasks automatically ignore this option for performance.
config.eager_load = true

# Full error reports are disabled and caching is turned on.
Expand All @@ -31,6 +31,9 @@
# Apache or NGINX already handles this.
config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present?

# This will affect assets in /public to be cached in Cloudfront
config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{1.year.seconds}" }

# Enable serving of images, stylesheets, and JavaScripts from an asset server.
# config.asset_host = "http://assets.example.com"

Expand All @@ -49,9 +52,6 @@

config.cache_store = :redis_cache_store, { url: config.redis_cache_url, pool_size: ENV.fetch("RAILS_MAX_THREADS", 5) }

# This will affect assets in /public to be cached in Cloudfront
config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{1.year.seconds}" }

# Use a real queuing backend for Active Job
# (and separate queues per environment)
# config.active_job.queue_adapter = :resque
Expand Down Expand Up @@ -85,6 +85,14 @@
# Do not dump schema after migrations.
config.active_record.dump_schema_after_migration = false

# Enable DNS rebinding protection and other `Host` header attacks.
# config.hosts = [
# "example.com", # Allow requests from example.com
# /.*\.example\.com/ # Allow requests from subdomains like `www.example.com`
# ]
# Skip DNS rebinding protection for the default health check endpoint.
# config.host_authorization = { exclude: ->(request) { request.path == "/up" } }

config.action_dispatch.trusted_proxies = [
ActionDispatch::RemoteIp::TRUSTED_PROXIES,
AWSIpRanges.cloudfront_ips.map { |proxy| IPAddr.new(proxy) },
Expand Down
12 changes: 9 additions & 3 deletions config/environments/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@
# Configure the domains permitted to access coordinates API
config.allowed_cors_origin = proc { "https://allowed.test.website" }

# `cache_classes` should be false under Spring as of Rails 7 - if we revisit our use of Spring,
# we should turn this back to true and stop setting `action_view.cache_template_loading`.
config.cache_classes = false
config.enable_reloading = true

config.action_view.cache_template_loading = true

# Eager loading loads your whole application. When running a single test locally,
Expand Down Expand Up @@ -54,6 +53,10 @@
# Raise exceptions for disallowed deprecations.
config.active_support.disallowed_deprecation = :raise

# Specify if an `ArgumentError` should be raised if `Rails.cache` `fetch` or
# `write` are given an invalid `expires_at` or `expires_in` time.
config.active_support.raise_on_invalid_cache_expiration_time = true

# Tell Active Support which deprecation messages to disallow.
config.active_support.disallowed_deprecation_warnings = []

Expand All @@ -69,6 +72,9 @@
config.middleware.insert_before 0, DfeSignIn::FakeSignOutEndpoint

config.log_file_size = 100.megabytes

# Raise error when a before_action's only/except options reference missing actions
config.action_controller.raise_on_missing_callback_actions = true
end

# Avoid OmniAuth output in tests:
Expand Down
7 changes: 7 additions & 0 deletions config/initializers/assets.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Be sure to restart your server when you modify this file.

# Version of your assets, change this if you want to expire all your assets.
Rails.application.config.assets.version = "1.0"

# Add additional assets to the asset load path.
# Rails.application.config.assets.paths << Emoji.images_path
35 changes: 0 additions & 35 deletions lib/message_encryptor.rb

This file was deleted.

5 changes: 4 additions & 1 deletion spec/configuration/filter_parameter_logging_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
specify "all anonymised analytics fields should be filtered from logs" do
analytics_hidden_pii.each_value do |shared|
shared.each do |field|
expect(filter_params).to include(field.to_sym)
matched = filter_params.any? do |pattern|
pattern.is_a?(Regexp) ? pattern.match?(field.to_s) : pattern == field.to_sym
end
expect(matched).to be(true), "Expected #{field} to be included in filter parameters"
end
end
end
Expand Down
20 changes: 0 additions & 20 deletions spec/lib/message_encryptor_spec.rb

This file was deleted.

Loading
Loading