Scope jobs and controllers by account
This commit is contained in:
committed by
Mike Dalessio
parent
b4f3ee5d86
commit
e0693de7c3
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
class Card::AutoPostponeAllDueJob < ApplicationJob
|
||||
def perform
|
||||
Card.auto_postpone_all_due
|
||||
end
|
||||
end
|
||||
@@ -1,5 +0,0 @@
|
||||
class Webhook::CleanupDeliveriesJob < ApplicationJob
|
||||
def perform
|
||||
Webhook::Delivery.cleanup
|
||||
end
|
||||
end
|
||||
@@ -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
|
||||
|
||||
|
||||
+15
-15
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<p class="txt-medium margin-none">Share the link below to invite people to this account</p>
|
||||
</header>
|
||||
|
||||
<% 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) %>
|
||||
<div class="flex align-center gap-half">
|
||||
<input type="text" class="input flex-item-grow" value="<%= url %>" readonly>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<p class="subtitle">Confirm your email address change</p>
|
||||
|
||||
<%= 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" %>
|
||||
|
||||
<p class="margin-block-start-double">If you didn’t request this change, you can ignore this email. Your email address WILL NOT be changed unless you hit the button.</p>
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
<div class="flex align-center gap">
|
||||
<div class="flex align-center gap input input--actor">
|
||||
<%= 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" %>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" id="log_in" class="btn btn--reversed center" data-form-target="submit">
|
||||
|
||||
@@ -7,6 +7,7 @@ production: &production
|
||||
# Application cleanup
|
||||
auto_postpone_all_due:
|
||||
class: Card::AutoPostponeAllDueJob
|
||||
command: "Card.auto_postpone_all_due"
|
||||
schedule: every hour at minute 50
|
||||
delete_unused_tags:
|
||||
class: DeleteUnusedTagsJob
|
||||
@@ -20,7 +21,7 @@ production: &production
|
||||
command: "SolidQueue::RecurringExecution.clear_in_batches"
|
||||
schedule: every hour at minute 52
|
||||
cleanup_webhook_deliveries:
|
||||
class: Webhook::CleanupDeliveriesJob
|
||||
command: "Webhook::Delivery.cleanup"
|
||||
schedule: every 4 hours at minute 51
|
||||
cleanup_magic_links:
|
||||
command: "MagicLink.cleanup"
|
||||
|
||||
+5
-2
@@ -134,8 +134,11 @@ Rails.application.routes.draw do
|
||||
|
||||
resources :qr_codes
|
||||
|
||||
get "join/:tenant/:code", to: "join_codes#new", as: :join
|
||||
post "join/:tenant/:code", to: "join_codes#create"
|
||||
# FIXME: Remove this before release
|
||||
get "join/:tenant/:code", to: redirect { |params, request| "/#{params[:tenant]}/join/#{params[:code]}" }
|
||||
|
||||
get "join/:code", to: "join_codes#new", as: :join
|
||||
post "join/:code", to: "join_codes#create"
|
||||
|
||||
namespace :users do
|
||||
resources :joins
|
||||
|
||||
@@ -2,23 +2,19 @@ require "test_helper"
|
||||
|
||||
class JoinCodesControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@tenant = accounts("37s").external_account_id
|
||||
@account = accounts("37s")
|
||||
@join_code = account_join_codes(:"37s")
|
||||
end
|
||||
|
||||
test "new" do
|
||||
untenanted do
|
||||
get join_path(tenant: @tenant, code: @join_code.code)
|
||||
end
|
||||
get join_path(code: @join_code.code, script_name: @account.slug)
|
||||
|
||||
assert_response :success
|
||||
assert_in_body "37signals"
|
||||
end
|
||||
|
||||
test "new with an invalid code" do
|
||||
untenanted do
|
||||
get join_path(tenant: @tenant, code: "INVALID-CODE")
|
||||
end
|
||||
get join_path(code: "INVALID-CODE", script_name: @account.slug)
|
||||
|
||||
assert_response :not_found
|
||||
end
|
||||
@@ -26,39 +22,31 @@ class JoinCodesControllerTest < ActionDispatch::IntegrationTest
|
||||
test "new with an inactive code" do
|
||||
@join_code.update!(usage_count: @join_code.usage_limit)
|
||||
|
||||
untenanted do
|
||||
get join_path(tenant: @tenant, code: @join_code.code)
|
||||
end
|
||||
get join_path(code: @join_code.code, script_name: @account.slug)
|
||||
|
||||
assert_response :not_found
|
||||
end
|
||||
|
||||
test "create" do
|
||||
untenanted do
|
||||
assert_difference -> { Identity.count }, 1 do
|
||||
assert_difference -> { User.count }, 1 do
|
||||
post join_path(tenant: @tenant, code: @join_code.code), params: { email_address: "new_user@example.com" }
|
||||
end
|
||||
assert_difference -> { Identity.count }, 1 do
|
||||
assert_difference -> { User.count }, 1 do
|
||||
post join_path(code: @join_code.code, script_name: @account.slug), params: { email_address: "new_user@example.com" }
|
||||
end
|
||||
|
||||
assert_redirected_to session_magic_link_path
|
||||
account = accounts("37s")
|
||||
assert_equal new_users_join_url(script_name: account.slug), session[:return_to_after_authenticating]
|
||||
end
|
||||
|
||||
assert_redirected_to session_magic_link_path
|
||||
assert_equal new_users_join_url(script_name: @account.slug), session[:return_to_after_authenticating]
|
||||
end
|
||||
|
||||
test "create for existing identity" do
|
||||
identity = identities(:jz)
|
||||
|
||||
untenanted do
|
||||
assert_no_difference -> { Identity.count } do
|
||||
assert_no_difference -> { User.count } do
|
||||
post join_path(tenant: @tenant, code: @join_code.code), params: { email_address: identity.email_address }
|
||||
end
|
||||
assert_no_difference -> { Identity.count } do
|
||||
assert_no_difference -> { User.count } do
|
||||
post join_path(code: @join_code.code, script_name: @account.slug), params: { email_address: identity.email_address }
|
||||
end
|
||||
|
||||
account = accounts("37s")
|
||||
assert_redirected_to landing_url(script_name: account.slug)
|
||||
end
|
||||
|
||||
assert_redirected_to landing_url(script_name: @account.slug)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -8,28 +8,22 @@ class Users::EmailAddresses::ConfirmationsControllerTest < ActionDispatch::Integ
|
||||
end
|
||||
|
||||
test "show" do
|
||||
untenanted do
|
||||
get user_email_address_confirmation_path(user_id: @user.id, email_address_token: @token)
|
||||
assert_response :success
|
||||
end
|
||||
get user_email_address_confirmation_path(user_id: @user.id, email_address_token: @token, script_name: @user.account.slug)
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "create" do
|
||||
old_email = @user.identity.email_address
|
||||
|
||||
untenanted do
|
||||
post user_email_address_confirmation_path(user_id: @user.id, email_address_token: @token)
|
||||
post user_email_address_confirmation_path(user_id: @user.id, email_address_token: @token, script_name: @user.account.slug)
|
||||
|
||||
assert_equal @new_email, @user.reload.identity.email_address
|
||||
assert_redirected_to edit_user_url(script_name: @user.account.slug, id: @user)
|
||||
end
|
||||
assert_equal @new_email, @user.reload.identity.email_address
|
||||
assert_redirected_to edit_user_url(script_name: @user.account.slug, id: @user)
|
||||
end
|
||||
|
||||
test "create with invalid token" do
|
||||
untenanted do
|
||||
assert_raises(ArgumentError) do
|
||||
post user_email_address_confirmation_path(user_id: @user.id, email_address_token: "invalid")
|
||||
end
|
||||
assert_raises(ArgumentError) do
|
||||
post user_email_address_confirmation_path(user_id: @user.id, email_address_token: "invalid", script_name: @user.account.slug)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -9,40 +9,32 @@ class Users::EmailAddressesControllerTest < ActionDispatch::IntegrationTest
|
||||
end
|
||||
|
||||
test "new" do
|
||||
untenanted do
|
||||
get new_user_email_address_path(@user)
|
||||
assert_response :success
|
||||
end
|
||||
get new_user_email_address_path(@user, script_name: @user.account.slug)
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "create" do
|
||||
untenanted do
|
||||
assert_emails 1 do
|
||||
post user_email_addresses_path(@user), params: { email_address: "newemail@example.com" }
|
||||
end
|
||||
assert_response :success
|
||||
assert_emails 1 do
|
||||
post user_email_addresses_path(@user, script_name: @user.account.slug), params: { email_address: "newemail@example.com" }
|
||||
end
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "create with existing email in same account" do
|
||||
existing_user = users(:kevin)
|
||||
existing_email = existing_user.identity.email_address
|
||||
|
||||
untenanted do
|
||||
post user_email_addresses_path(@user), params: { email_address: existing_email }
|
||||
assert_redirected_to new_user_email_address_path(@user)
|
||||
assert_equal "You already have a user in this account with that email address", flash[:alert]
|
||||
end
|
||||
post user_email_addresses_path(@user, script_name: @user.account.slug), params: { email_address: existing_email }
|
||||
assert_redirected_to new_user_email_address_path(@user)
|
||||
assert_equal "You already have a user in this account with that email address", flash[:alert]
|
||||
end
|
||||
|
||||
test "create for other user" do
|
||||
other_user = users(:kevin)
|
||||
|
||||
untenanted do
|
||||
assert_no_emails do
|
||||
post user_email_addresses_path(other_user), params: { email_address: "newemail@example.com" }
|
||||
end
|
||||
assert_response :not_found
|
||||
assert_no_emails do
|
||||
post user_email_addresses_path(other_user, script_name: @user.account.slug), params: { email_address: "newemail@example.com" }
|
||||
end
|
||||
assert_response :not_found
|
||||
end
|
||||
end
|
||||
|
||||
@@ -35,21 +35,21 @@ class User::EmailAddressChangeableTest < ActiveSupport::TestCase
|
||||
test "change_email_address_using_token" do
|
||||
token = @user.send(:generate_email_address_change_token, to: @new_email)
|
||||
|
||||
User.change_email_address_using_token(token)
|
||||
@user.change_email_address_using_token(token)
|
||||
|
||||
assert_equal @new_email, @user.reload.identity.email_address
|
||||
end
|
||||
|
||||
test "change_email_address_using_token with invalid token" do
|
||||
assert_raises(ArgumentError, match: /invalid/) do
|
||||
User.change_email_address_using_token("invalid_token")
|
||||
@user.change_email_address_using_token("invalid_token")
|
||||
end
|
||||
|
||||
token = @user.send(:generate_email_address_change_token, to: @new_email)
|
||||
@identity.update!(email_address: "different@example.com")
|
||||
|
||||
assert_raises(ArgumentError, match: /different email address/) do
|
||||
User.change_email_address_using_token(token)
|
||||
@user.change_email_address_using_token(token)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user