Merge branch 'main' into mobile-columns-pt-iii
* main: Only hide the blank slate when there are no cards present Support local installations where the app is loaded over HTTP Revert "Make sure new card drafts are refreshed when reused" Make sure new card drafts are refreshed when reused Add a "*" to the queues handled by the solid queue worker pool Remove unnecessary recurring execution cleanup task Refactor: Replace pluck(:id) with ids method
This commit is contained in:
@@ -24,12 +24,12 @@
|
||||
border: 2px dashed var(--color-ink-light);
|
||||
border-radius: 1ch;
|
||||
color: var(--color-ink-dark);
|
||||
margin-block-start: 5dvh;
|
||||
margin-block-start: 2dvh;
|
||||
padding: 1ch 2ch;
|
||||
rotate: -3deg;
|
||||
font-weight: 500;
|
||||
|
||||
.cards:not(.cards--grid) & {
|
||||
.cards:has(.card) & {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class BoardsController < ApplicationController
|
||||
end
|
||||
|
||||
def edit
|
||||
selected_user_ids = @board.users.pluck :id
|
||||
selected_user_ids = @board.users.ids
|
||||
@selected_users, @unselected_users = \
|
||||
@board.account.users.active.alphabetically.includes(:identity).partition { |user| selected_user_ids.include? user.id }
|
||||
end
|
||||
|
||||
@@ -2,31 +2,19 @@ module RequestForgeryProtection
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
after_action :append_sec_fetch_site_to_vary_header
|
||||
protect_from_forgery using: :header_only, with: :exception
|
||||
end
|
||||
|
||||
private
|
||||
def append_sec_fetch_site_to_vary_header
|
||||
vary_header = response.headers["Vary"].to_s.split(",").map(&:strip).reject(&:blank?)
|
||||
response.headers["Vary"] = (vary_header + [ "Sec-Fetch-Site" ]).join(",")
|
||||
def verified_via_header_only?
|
||||
super || allowed_api_request? || allowed_insecure_context_request?
|
||||
end
|
||||
|
||||
def verified_request?
|
||||
request.get? || request.head? || !protect_against_forgery? ||
|
||||
(valid_request_origin? && safe_fetch_site?)
|
||||
def allowed_api_request?
|
||||
sec_fetch_site_value.nil? && request.format.json?
|
||||
end
|
||||
|
||||
SAFE_FETCH_SITES = %w[ same-origin same-site ]
|
||||
|
||||
def safe_fetch_site?
|
||||
SAFE_FETCH_SITES.include?(sec_fetch_site_value) || (sec_fetch_site_value.nil? && api_request?)
|
||||
end
|
||||
|
||||
def api_request?
|
||||
request.format.json?
|
||||
end
|
||||
|
||||
def sec_fetch_site_value
|
||||
request.headers["Sec-Fetch-Site"].to_s.downcase.presence
|
||||
def allowed_insecure_context_request?
|
||||
sec_fetch_site_value.nil? && !request.ssl? && !Rails.configuration.force_ssl
|
||||
end
|
||||
end
|
||||
|
||||
@@ -21,7 +21,7 @@ module Board::Storage
|
||||
end
|
||||
|
||||
def card_ids
|
||||
@card_ids ||= cards.pluck(:id)
|
||||
@card_ids ||= cards.ids
|
||||
end
|
||||
|
||||
def card_image_bytes
|
||||
@@ -36,7 +36,7 @@ module Board::Storage
|
||||
|
||||
def comment_embed_bytes
|
||||
card_ids.each_slice(BATCH_SIZE).sum do |batch|
|
||||
sum_embed_bytes_for "Comment", Comment.where(card_id: batch).pluck(:id)
|
||||
sum_embed_bytes_for "Comment", Comment.where(card_id: batch).ids
|
||||
end
|
||||
end
|
||||
|
||||
@@ -46,8 +46,7 @@ module Board::Storage
|
||||
|
||||
def sum_embed_bytes_for(record_type, record_ids)
|
||||
rich_text_ids = ActionText::RichText \
|
||||
.where(record_type: record_type, record_id: record_ids)
|
||||
.pluck(:id)
|
||||
.where(record_type: record_type, record_id: record_ids).ids
|
||||
|
||||
sum_blob_bytes_in_batches \
|
||||
ActiveStorage::Attachment.where(record_type: "ActionText::RichText", name: "embeds"),
|
||||
|
||||
@@ -13,6 +13,6 @@ module User::Accessor
|
||||
|
||||
private
|
||||
def grant_access_to_boards
|
||||
Access.insert_all account.boards.all_access.pluck(:id).collect { |board_id| { id: ActiveRecord::Type::Uuid.generate, board_id: board_id, user_id: id, account_id: account.id } }
|
||||
Access.insert_all account.boards.all_access.ids.collect { |board_id| { id: ActiveRecord::Type::Uuid.generate, board_id: board_id, user_id: id, account_id: account.id } }
|
||||
end
|
||||
end
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ default: &default
|
||||
- polling_interval: 1
|
||||
batch_size: 500
|
||||
workers:
|
||||
- queues: [ "default", "solid_queue_recurring", "backend", "webhooks" ]
|
||||
- queues: [ "default", "solid_queue_recurring", "backend", "webhooks", "*" ]
|
||||
threads: 3
|
||||
processes: <%= Integer(ENV.fetch("JOB_CONCURRENCY") { Concurrent.physical_processor_count }) %>
|
||||
polling_interval: 0.1
|
||||
|
||||
@@ -18,9 +18,6 @@ production: &production
|
||||
clear_solid_queue_finished_jobs:
|
||||
command: "SolidQueue::Job.clear_finished_in_batches(sleep_between_batches: 0.3)"
|
||||
schedule: every hour at minute 12
|
||||
clear_solid_queue_recurring_executions:
|
||||
command: "SolidQueue::RecurringExecution.clear_in_batches"
|
||||
schedule: every hour at minute 52
|
||||
cleanup_webhook_deliveries:
|
||||
command: "Webhook::Delivery.cleanup"
|
||||
schedule: every 4 hours at minute 51
|
||||
@@ -43,9 +40,6 @@ beta:
|
||||
clear_solid_queue_finished_jobs:
|
||||
command: "SolidQueue::Job.clear_finished_in_batches(sleep_between_batches: 0.3)"
|
||||
schedule: every hour at minute 12
|
||||
clear_solid_queue_recurring_executions:
|
||||
command: "SolidQueue::RecurringExecution.clear_in_batches"
|
||||
schedule: every hour at minute 52
|
||||
|
||||
staging: *production
|
||||
development: *production
|
||||
|
||||
@@ -132,6 +132,7 @@ ActiveRecord::Schema[8.2].define(version: 1) do
|
||||
t.index ["key"], name: "index_solid_queue_semaphores_on_key", unique: true
|
||||
end
|
||||
|
||||
# If these FKs are removed, make sure to periodically run `RecurringExecution.clear_in_batches`
|
||||
add_foreign_key "solid_queue_blocked_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
|
||||
add_foreign_key "solid_queue_claimed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
|
||||
add_foreign_key "solid_queue_failed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
|
||||
|
||||
@@ -6,83 +6,16 @@ class RequestForgeryProtectionTest < ActionDispatch::IntegrationTest
|
||||
|
||||
@original_allow_forgery_protection = ActionController::Base.allow_forgery_protection
|
||||
ActionController::Base.allow_forgery_protection = true
|
||||
|
||||
@original_force_ssl = Rails.configuration.force_ssl
|
||||
end
|
||||
|
||||
teardown do
|
||||
ActionController::Base.allow_forgery_protection = @original_allow_forgery_protection
|
||||
Rails.configuration.force_ssl = @original_force_ssl
|
||||
end
|
||||
|
||||
test "fails if Sec-Fetch-Site is cross-site" do
|
||||
assert_no_difference -> { Board.count } do
|
||||
post boards_path,
|
||||
params: { board: { name: "Test Board" } },
|
||||
headers: { "Sec-Fetch-Site" => "cross-site" }
|
||||
end
|
||||
|
||||
assert_response :unprocessable_entity
|
||||
end
|
||||
|
||||
test "succeeds with same-origin Sec-Fetch-Site" do
|
||||
assert_difference -> { Board.count }, +1 do
|
||||
post boards_path,
|
||||
params: { board: { name: "Test Board" } },
|
||||
headers: { "Sec-Fetch-Site" => "same-origin" }
|
||||
end
|
||||
|
||||
assert_response :redirect
|
||||
end
|
||||
|
||||
test "succeeds with same-site Sec-Fetch-Site" do
|
||||
assert_difference -> { Board.count }, +1 do
|
||||
post boards_path,
|
||||
params: { board: { name: "Test Board" } },
|
||||
headers: { "Sec-Fetch-Site" => "same-site" }
|
||||
end
|
||||
|
||||
assert_response :redirect
|
||||
end
|
||||
|
||||
test "fails with none Sec-Fetch-Site" do
|
||||
assert_no_difference -> { Board.count } do
|
||||
post boards_path,
|
||||
params: { board: { name: "Test Board" } },
|
||||
headers: { "Sec-Fetch-Site" => "none" }
|
||||
end
|
||||
|
||||
assert_response :unprocessable_entity
|
||||
end
|
||||
|
||||
test "fails when Sec-Fetch-Site header is missing" do
|
||||
assert_no_difference -> { Board.count } do
|
||||
post boards_path, params: { board: { name: "Test Board" } }
|
||||
end
|
||||
|
||||
assert_response :unprocessable_entity
|
||||
end
|
||||
|
||||
test "GET requests succeed regardless of Sec-Fetch-Site header" do
|
||||
get board_path(boards(:writebook)), headers: { "Sec-Fetch-Site" => "cross-site" }
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "appends Sec-Fetch-Site to Vary header on GET requests" do
|
||||
get board_path(boards(:writebook))
|
||||
|
||||
assert_response :success
|
||||
assert_includes response.headers["Vary"], "Sec-Fetch-Site"
|
||||
end
|
||||
|
||||
test "appends Sec-Fetch-Site to Vary header on POST requests" do
|
||||
post boards_path,
|
||||
params: { board: { name: "Test Board" } },
|
||||
headers: { "Sec-Fetch-Site" => "same-origin" }
|
||||
|
||||
assert_response :redirect
|
||||
assert_includes response.headers["Vary"], "Sec-Fetch-Site"
|
||||
end
|
||||
|
||||
test "JSON request succeeds with missing Sec-Fetch-Site" do
|
||||
test "JSON request succeeds with missing Sec-Fetch-Site header" do
|
||||
assert_difference -> { Board.count }, +1 do
|
||||
post boards_path,
|
||||
params: { board: { name: "Test Board" } },
|
||||
@@ -92,22 +25,37 @@ class RequestForgeryProtectionTest < ActionDispatch::IntegrationTest
|
||||
assert_response :created
|
||||
end
|
||||
|
||||
test "JSON request fails with cross-site Sec-Fetch-Site" do
|
||||
test "HTTP request succeeds with missing Sec-Fetch-Site header when force_ssl is disabled" do
|
||||
Rails.configuration.force_ssl = false
|
||||
|
||||
assert_difference -> { Board.count }, +1 do
|
||||
post boards_path,
|
||||
params: { board: { name: "Test Board" } }
|
||||
end
|
||||
|
||||
assert_response :redirect
|
||||
end
|
||||
|
||||
test "HTTP request fails with missing Sec-Fetch-Site header when force_ssl is enabled" do
|
||||
Rails.configuration.force_ssl = true
|
||||
|
||||
assert_no_difference -> { Board.count } do
|
||||
post boards_path,
|
||||
params: { board: { name: "Test Board" } },
|
||||
headers: { "Sec-Fetch-Site" => "cross-site" },
|
||||
as: :json
|
||||
params: { board: { name: "Test Board" } }
|
||||
end
|
||||
|
||||
assert_response :unprocessable_entity
|
||||
end
|
||||
|
||||
private
|
||||
def csrf_token
|
||||
@csrf_token ||= begin
|
||||
get new_board_path
|
||||
response.body[/name="authenticity_token" value="([^"]+)"/, 1]
|
||||
end
|
||||
test "HTTPS request fails with missing Sec-Fetch-Site header" do
|
||||
Rails.configuration.force_ssl = false
|
||||
|
||||
assert_no_difference -> { Board.count } do
|
||||
post boards_path,
|
||||
params: { board: { name: "Test Board" } },
|
||||
headers: { "X-Forwarded-Proto" => "https" }
|
||||
end
|
||||
|
||||
assert_response :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user