From e0693de7c3879c04013a930fb8f9ab144e813432 Mon Sep 17 00:00:00 2001 From: "Stanko K.R." Date: Fri, 14 Nov 2025 12:24:13 +0100 Subject: [PATCH] Scope jobs and controllers by account --- app/controllers/cards/taggings_controller.rb | 2 +- app/controllers/concerns/column_scoped.rb | 2 +- app/controllers/join_codes_controller.rb | 18 ++------ app/controllers/my/menus_controller.rb | 2 +- app/controllers/prompts/tags_controller.rb | 2 +- app/controllers/qr_codes_controller.rb | 7 +++- app/controllers/users/avatars_controller.rb | 4 +- .../confirmations_controller.rb | 5 +-- .../users/email_addresses_controller.rb | 4 -- .../webhooks/activations_controller.rb | 2 +- app/jobs/card/auto_postpone_all_due_job.rb | 5 --- app/jobs/webhook/cleanup_deliveries_job.rb | 5 --- app/models/account.rb | 5 ++- app/models/qr_code_link.rb | 30 ++++++------- app/models/user/email_address_changeable.rb | 25 +++++------ app/views/account/join_codes/show.html.erb | 2 +- .../email_change_confirmation.html.erb | 2 +- app/views/users/edit.html.erb | 2 +- config/recurring.yml | 3 +- config/routes.rb | 7 +++- .../controllers/join_codes_controller_test.rb | 42 +++++++------------ .../confirmations_controller_test.rb | 20 ++++----- .../users/email_addresses_controller_test.rb | 30 +++++-------- .../user/email_address_changeable_test.rb | 6 +-- 24 files changed, 95 insertions(+), 137 deletions(-) delete mode 100644 app/jobs/card/auto_postpone_all_due_job.rb delete mode 100644 app/jobs/webhook/cleanup_deliveries_job.rb diff --git a/app/controllers/cards/taggings_controller.rb b/app/controllers/cards/taggings_controller.rb index 16227011d..d388fae22 100644 --- a/app/controllers/cards/taggings_controller.rb +++ b/app/controllers/cards/taggings_controller.rb @@ -2,7 +2,7 @@ class Cards::TaggingsController < ApplicationController include CardScoped def new - @tags = Tag.all.alphabetically + @tags = Current.account.tags.all.alphabetically fresh_when etag: [ @tags, @card.tags ] end diff --git a/app/controllers/concerns/column_scoped.rb b/app/controllers/concerns/column_scoped.rb index ea002b95a..92d0715fa 100644 --- a/app/controllers/concerns/column_scoped.rb +++ b/app/controllers/concerns/column_scoped.rb @@ -7,6 +7,6 @@ module ColumnScoped private def set_column - @column = Column.find(params[:column_id]) + @column = Current.account.columns.find(params[:column_id]) end end diff --git a/app/controllers/join_codes_controller.rb b/app/controllers/join_codes_controller.rb index 62f3c1ca9..a3aa501fc 100644 --- a/app/controllers/join_codes_controller.rb +++ b/app/controllers/join_codes_controller.rb @@ -1,5 +1,4 @@ class JoinCodesController < ApplicationController - disallow_account_scope allow_unauthenticated_access before_action :set_join_code @@ -30,22 +29,13 @@ class JoinCodesController < ApplicationController private def set_join_code - # TODO:PLANB: this find should be scoped by account - # 2025-11-12 [Stanko]: I think that we don't have to scope this by account as the code - # is globally unique and indexed. Except if we need to scope it - # for some other reason? - @join_code ||= Account::JoinCode.active.find_by(code: code) + @join_code ||= Account::JoinCode.active.find_by( + code: params.expect(:code), + account: Current.account + ) end def ensure_join_code_is_valid head :not_found unless @join_code&.active? end - - def tenant - params.expect(:tenant) - end - - def code - params.expect(:code) - end end diff --git a/app/controllers/my/menus_controller.rb b/app/controllers/my/menus_controller.rb index dd04aae67..7a5754593 100644 --- a/app/controllers/my/menus_controller.rb +++ b/app/controllers/my/menus_controller.rb @@ -2,7 +2,7 @@ class My::MenusController < ApplicationController def show @filters = Current.user.filters.all @boards = Current.user.boards.ordered_by_recently_accessed - @tags = Tag.all.alphabetically + @tags = Current.account.tags.all.alphabetically @users = Current.account.users.active.alphabetically fresh_when etag: [ @filters, @boards, @tags, @users ] diff --git a/app/controllers/prompts/tags_controller.rb b/app/controllers/prompts/tags_controller.rb index 5a90b23fb..3d32a071e 100644 --- a/app/controllers/prompts/tags_controller.rb +++ b/app/controllers/prompts/tags_controller.rb @@ -1,6 +1,6 @@ class Prompts::TagsController < ApplicationController def index - @tags = Tag.all.alphabetically + @tags = Current.account.tags.all.alphabetically if stale? etag: @tags render layout: false diff --git a/app/controllers/qr_codes_controller.rb b/app/controllers/qr_codes_controller.rb index 42917d64b..aa8ca8334 100644 --- a/app/controllers/qr_codes_controller.rb +++ b/app/controllers/qr_codes_controller.rb @@ -3,6 +3,11 @@ class QrCodesController < ApplicationController def show expires_in 1.year, public: true - render svg: RQRCode::QRCode.new(QrCodeLink.from_signed(params[:id]).url).as_svg(viewbox: true, fill: :white, color: :black) + + qr_code_svg = RQRCode::QRCode + .new(QrCodeLink.from_signed(params[:id]).url) + .as_svg(viewbox: true, fill: :white, color: :black) + + render svg: qr_code_svg end end diff --git a/app/controllers/users/avatars_controller.rb b/app/controllers/users/avatars_controller.rb index 8be049cc3..425c194b1 100644 --- a/app/controllers/users/avatars_controller.rb +++ b/app/controllers/users/avatars_controller.rb @@ -19,11 +19,11 @@ class Users::AvatarsController < ApplicationController private def set_user - @user = User.find(params[:user_id]) + @user = Current.account.users.find(params[:user_id]) end def ensure_permission_to_administer_user - head :forbidden unless Current.user.admin? || Current.user == @user + head :forbidden unless Current.user.can_change?(@user) end def render_avatar_or_initials diff --git a/app/controllers/users/email_addresses/confirmations_controller.rb b/app/controllers/users/email_addresses/confirmations_controller.rb index 10ad9ffed..dad998671 100644 --- a/app/controllers/users/email_addresses/confirmations_controller.rb +++ b/app/controllers/users/email_addresses/confirmations_controller.rb @@ -1,5 +1,4 @@ class Users::EmailAddresses::ConfirmationsController < ApplicationController - disallow_account_scope allow_unauthenticated_access before_action :set_user @@ -9,7 +8,7 @@ class Users::EmailAddresses::ConfirmationsController < ApplicationController end def create - user = User.change_email_address_using_token(token) + user = @user.change_email_address_using_token(token) terminate_session if Current.session start_new_session_for user.identity @@ -19,7 +18,7 @@ class Users::EmailAddresses::ConfirmationsController < ApplicationController private def set_user - @user = User.find(params[:user_id]) + @user = Current.account.users.active.find(params[:user_id]) end def token diff --git a/app/controllers/users/email_addresses_controller.rb b/app/controllers/users/email_addresses_controller.rb index 691c50e42..69957b1f6 100644 --- a/app/controllers/users/email_addresses_controller.rb +++ b/app/controllers/users/email_addresses_controller.rb @@ -1,8 +1,4 @@ class Users::EmailAddressesController < ApplicationController - disallow_account_scope - - layout "public" - before_action :set_user rate_limit to: 5, within: 1.hour, only: :create diff --git a/app/controllers/webhooks/activations_controller.rb b/app/controllers/webhooks/activations_controller.rb index 87b05cf91..cbe3e9e32 100644 --- a/app/controllers/webhooks/activations_controller.rb +++ b/app/controllers/webhooks/activations_controller.rb @@ -2,7 +2,7 @@ class Webhooks::ActivationsController < ApplicationController before_action :ensure_admin def create - webhook = Webhook.find(params[:webhook_id]) + webhook = Current.account.webhooks.find(params[:webhook_id]) webhook.activate redirect_to webhook diff --git a/app/jobs/card/auto_postpone_all_due_job.rb b/app/jobs/card/auto_postpone_all_due_job.rb deleted file mode 100644 index 684e2cbe2..000000000 --- a/app/jobs/card/auto_postpone_all_due_job.rb +++ /dev/null @@ -1,5 +0,0 @@ -class Card::AutoPostponeAllDueJob < ApplicationJob - def perform - Card.auto_postpone_all_due - end -end diff --git a/app/jobs/webhook/cleanup_deliveries_job.rb b/app/jobs/webhook/cleanup_deliveries_job.rb deleted file mode 100644 index 37439de91..000000000 --- a/app/jobs/webhook/cleanup_deliveries_job.rb +++ /dev/null @@ -1,5 +0,0 @@ -class Webhook::CleanupDeliveriesJob < ApplicationJob - def perform - Webhook::Delivery.cleanup - end -end diff --git a/app/models/account.rb b/app/models/account.rb index ee2d6d85a..5916bc690 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -4,7 +4,10 @@ class Account < ApplicationRecord has_one :join_code has_many :users, dependent: :destroy has_many :boards, dependent: :destroy - has_many :cards, through: :boards + has_many :cards, dependent: :destroy + has_many :webhooks, dependent: :destroy + has_many :tags, dependent: :destroy + has_many :columns, dependent: :destroy has_many_attached :uploads diff --git a/app/models/qr_code_link.rb b/app/models/qr_code_link.rb index 81a65fa25..8fc5a2b4d 100644 --- a/app/models/qr_code_link.rb +++ b/app/models/qr_code_link.rb @@ -1,6 +1,21 @@ class QrCodeLink attr_reader :url + class << self + def from_signed(signed) + new verifier.verify(signed, purpose: :qr_code) + end + + def verifier + ActiveSupport::MessageVerifier.new(secret, url_safe: true) + end + + private + def secret + Rails.application.key_generator.generate_key("qr_codes") + end + end + def initialize(url) @url = url end @@ -8,19 +23,4 @@ class QrCodeLink def signed self.class.verifier.generate(@url, purpose: :qr_code) end - - def self.from_signed(signed) - new verifier.verify(signed, purpose: :qr_code) - end - - private - class << self - def verifier - ActiveSupport::MessageVerifier.new(secret, url_safe: true) - end - - def secret - Rails.application.key_generator.generate_key("qr_codes") - end - end end diff --git a/app/models/user/email_address_changeable.rb b/app/models/user/email_address_changeable.rb index 80e194231..4d93553d0 100644 --- a/app/models/user/email_address_changeable.rb +++ b/app/models/user/email_address_changeable.rb @@ -4,21 +4,18 @@ module User::EmailAddressChangeable extend ActiveSupport::Concern - class_methods do - def change_email_address_using_token(token) - parsed_token = SignedGlobalID.parse(token, for: EMAIL_CHANGE_TOKEN_PURPOSE) - user = parsed_token&.find + def change_email_address_using_token(token) + parsed_token = SignedGlobalID.parse(token, for: EMAIL_CHANGE_TOKEN_PURPOSE) - if parsed_token.nil? - raise ArgumentError, "The token is invalid" - elsif user.nil? - raise ArgumentError, "The user no longer exists" - elsif user.identity.email_address != parsed_token.params.fetch("old_email_address") - raise ArgumentError, "The token was generated for a different email address" - else - new_email_address = parsed_token.params.fetch("new_email_address") - user.change_email_address(new_email_address) - end + if parsed_token.nil? + raise ArgumentError, "The token is invalid" + elsif parsed_token.find != self + raise ArgumentError, "The token was generated for a different user" + elsif identity.email_address != parsed_token.params.fetch("old_email_address") + raise ArgumentError, "The token was generated for a different email address" + else + new_email_address = parsed_token.params.fetch("new_email_address") + change_email_address(new_email_address) end end diff --git a/app/views/account/join_codes/show.html.erb b/app/views/account/join_codes/show.html.erb index 9e8a17425..da1527501 100644 --- a/app/views/account/join_codes/show.html.erb +++ b/app/views/account/join_codes/show.html.erb @@ -17,7 +17,7 @@

Share the link below to invite people to this account

- <% url = join_url(code: @join_code.code, tenant: Current.account.external_account_id, script_name: nil) %> + <% url = join_url(code: @join_code.code, script_name: Current.account.slug) %>
diff --git a/app/views/mailers/identity_mailer/email_change_confirmation.html.erb b/app/views/mailers/identity_mailer/email_change_confirmation.html.erb index 43782efb1..8ab21b43b 100644 --- a/app/views/mailers/identity_mailer/email_change_confirmation.html.erb +++ b/app/views/mailers/identity_mailer/email_change_confirmation.html.erb @@ -1,6 +1,6 @@

Confirm your email address change

-<%= link_to "Yes use use this email address", user_email_address_confirmation_url(user_id: @user.id, email_address_token: @token), class: "btn" %> +<%= link_to "Yes use use this email address", user_email_address_confirmation_url(script_name: @user.account.slug, user_id: @user.id, email_address_token: @token), class: "btn" %>

If you didn’t request this change, you can ignore this email. Your email address WILL NOT be changed unless you hit the button.

diff --git a/app/views/users/edit.html.erb b/app/views/users/edit.html.erb index 4dd365d23..5ac5e7807 100644 --- a/app/views/users/edit.html.erb +++ b/app/views/users/edit.html.erb @@ -40,7 +40,7 @@
<%= form.email_field :email_address, class: "input full-width", autocomplete: "username", placeholder: "Email address", required: true, readonly: true, value: @user.identity.email_address %> - <%= link_to "Change email", new_user_email_address_url(script_name: nil, user_id: Current.user.id), class: "btn btn--plain txt-link txt-small txt-nowrap" %> + <%= link_to "Change email", new_user_email_address_path(user_id: Current.user.id), class: "btn btn--plain txt-link txt-small txt-nowrap" %>