Skip to content

Commit

Permalink
Introduce Current per-request attributes singleton
Browse files Browse the repository at this point in the history
This creates an idiomatic Rails per-request attributes singleton
`Current`, using [`ActiveSupport::CurrentAttributes`], and stores the
current user in it. This will allow us to access the currently active
user in cross-cutting concerns, without having to deeply pass it around.

`CurrentAttributes` is a "sharp tool" that should be used with caution
as it creates global state for a request, but is an ideal tool for
dealing with auditing and logging concerns (which we'll soon introduce)
where you need to have global awareness of certain request attributes
outside of just the controller layer.

We have prior art for this pattern, for example in Whitehall.

[`ActiveSupport::CurrentAttributes`]: https://edgeapi.rubyonrails.org/classes/ActiveSupport/CurrentAttributes.html
  • Loading branch information
csutter committed Feb 5, 2025
1 parent b8fb3a3 commit 51a1f04
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 6 deletions.
5 changes: 2 additions & 3 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
class ApplicationController < ActionController::Base
include AuthenticatesUser

# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception

include GDS::SSO::ControllerMethods
before_action :authenticate_user!
end
14 changes: 14 additions & 0 deletions app/controllers/concerns/authenticates_user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Enforces user authentication for a controller and stores the authenticated user in `Current`
module AuthenticatesUser
extend ActiveSupport::Concern

included do
include GDS::SSO::ControllerMethods

before_action do
authenticate_user!

Current.user = current_user
end
end
end
2 changes: 1 addition & 1 deletion app/controllers/recommended_links_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,6 @@ def set_recommended_link
def recommended_link_params
params
.expect(recommended_link: %i[link title description keywords comment])
.merge(user_id: current_user.id)
.merge(user_id: Current.user.id)
end
end
4 changes: 2 additions & 2 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module ApplicationHelper
def navigation_items
return [] unless current_user
return [] unless Current.user?

[
{
Expand All @@ -14,7 +14,7 @@ def navigation_items
active: controller.controller_name == "controls",
},
{
text: current_user.name,
text: Current.user.name,
href: Plek.new.external_url_for("signon"),
},
{
Expand Down
8 changes: 8 additions & 0 deletions app/models/current.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Stores cross-cutting concerns for every request
class Current < ActiveSupport::CurrentAttributes
attribute :user

def user?
user.present?
end
end

0 comments on commit 51a1f04

Please sign in to comment.