Introduce untenanted Identity and Membership models

The new integration test shows the desired user-facing behavior, which
is to make it easy to login without a tenanted URL and to jump between
tenants.

Note that we track two things in the identity_token cookie: a signed
id, and the updated_at for the underlying Identity object. This allows
us to effectively cache on the Identity without having to hit the
database, by using an Identity::Mock object that is compatible with
etag and cache methods.
This commit is contained in:
Mike Dalessio
2025-10-09 17:23:31 -04:00
committed by Stanko K.R.
parent 4c851e2756
commit 1277cc065b
4 changed files with 15 additions and 9 deletions
+2 -3
View File
@@ -86,13 +86,12 @@ module Authentication
def link_identity(user)
token_value = cookies.signed[:identity_token]
token_identity = Identity.find_signed(token_value["id"]) if token_value.present?
identity = user.set_identity(token_identity)
identity = Identity.find_signed(token_value["id"]) if token_value.present?
identity = user.set_identity(identity)
cookies.signed.permanent[:identity_token] = { value: { "id" => identity.signed_id, "updated_at" => identity.updated_at }, httponly: true, same_site: :lax }
end
def set_current_identity_token
link_identity(Current.user) if cookies.signed[:identity_token].nil? && Current.user.present?
Current.identity_token = Identity::Mock.new(**cookies.signed[:identity_token])
end
+6
View File
@@ -5,6 +5,12 @@
<%= filter_place_menu_item notifications_path, "Notifications", "bell" %>
<%= filter_place_menu_item notifications_settings_path, "Notification Settings", "settings" %>
<% cache [ Current.user, Current.identity_token ] do %>
<% Current.user.identity.memberships.where.not(user_tenant: Current.user.tenant).each do |membership| %>
<%= filter_place_menu_item root_url(script_name: "/#{membership.user_tenant}"), "#{membership.account_name}", "logo-color", new_window: true %>
<% end %>
<% end %>
<%= tag.li class: "popup__item", data: { filter_target: "item", navigable_list_target: "item" } do %>
<%= icon_tag "logout", class: "popup__icon" %>
<%= button_to session_path, method: :delete, class: "popup__btn btn", data: { turbo: false } do %>
+3 -2
View File
@@ -3,6 +3,7 @@
<div class="panel shadow center margin-block-double" style="--panel-size: 55ch;">
<h1 class="txt-xx-large margin-block-end-double">BOXCAR</h1>
<% cache [ Current.user, Current.identity_token ] do %>
<% identity = Identity.find_signed(Current.identity_token.id) %>
<% memberships = identity&.memberships %>
<% if memberships.present? %>
@@ -10,11 +11,11 @@
<ul class="flex flex-column gap txt-large" style="list-style-type: none; padding: 0;">
<% memberships.each do |membership| %>
<li>
<%= link_to membership.account_name, root_url(script_name: "/#{membership.user_tenant}"), class: "btn btn--reversed center" %>
<%= link_to membership.account_name, root_url(script_name: "/#{membership.user_tenant}") %>: <%= membership.email_address %>
</li>
<% end %>
</ul>
<% else %>
<p class="txt-large txt-align-center txt-subtle">You don't have any existing BOXCAR accounts.</p>
<% end %>
</div>
<% end %>
+4 -4
View File
@@ -24,14 +24,14 @@ class IdentityMembershipTest < ActionDispatch::IntegrationTest
# Render links for other Fizzies in the jump menu
get my_menu_path(script_name: @tenant_paths[0])
assert_select "#my_menu ul li a[href='#{@tenant_urls[1]}']", "Account for #{@tenants[1]}"
assert_select "#my_menu ul li a[href='#{@tenant_urls[1]}']", "Account for #{@tenants[1]}: user@example.com"
get my_menu_path(script_name: @tenant_paths[1])
assert_select "#my_menu ul li a[href='#{@tenant_urls[0]}']", "Account for #{@tenants[0]}"
assert_select "#my_menu ul li a[href='#{@tenant_urls[0]}']", "Account for #{@tenants[0]}: user@example.com"
# Render links for all the identity's Fizzies
get root_path(script_name: nil)
assert_select "ul li a[href='#{@tenant_urls[0]}']", "Account for #{@tenants[0]}"
assert_select "ul li a[href='#{@tenant_urls[1]}']", "Account for #{@tenants[1]}"
assert_select "ul li a[href='#{@tenant_urls[0]}']", "Account for #{@tenants[0]}: user@example.com"
assert_select "ul li a[href='#{@tenant_urls[1]}']", "Account for #{@tenants[1]}: user@example.com"
end
end