Merge pull request #120 from basecamp/allow-restricting-new-room-creation-to-admins

Add new has_json to add Account#settings to restrict room creation to only administrators
This commit is contained in:
Stanko Krtalić
2025-12-02 08:27:29 +01:00
committed by GitHub
16 changed files with 88 additions and 7 deletions

View File

@@ -4,9 +4,11 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
# Rails
gem "rails", github: "rails/rails", branch: "main"
gem "ostruct"
gem "benchmark"
# Drivers
gem "sqlite3", "~> 2.7"
gem "sqlite3"
gem "redis", "~> 5.4"
# Deployment

View File

@@ -135,6 +135,7 @@ GEM
ast (2.4.3)
base64 (0.3.0)
bcrypt (3.1.20)
benchmark (0.5.0)
bigdecimal (3.3.1)
brakeman (7.1.1)
racc
@@ -239,6 +240,7 @@ GEM
nokogiri (1.18.10-x86_64-linux-gnu)
racc (~> 1.4)
openssl (3.3.0)
ostruct (0.6.3)
parallel (1.27.0)
parser (3.3.9.0)
ast (~> 2.4.1)
@@ -408,6 +410,7 @@ PLATFORMS
DEPENDENCIES
bcrypt
benchmark
brakeman
capybara
debug
@@ -419,6 +422,7 @@ DEPENDENCIES
kredis
mocha
net-http-persistent
ostruct
platform_agent
propshaft!
puma (~> 6.6)
@@ -432,7 +436,7 @@ DEPENDENCIES
selenium-webdriver
sentry-rails
sentry-ruby
sqlite3 (~> 2.7)
sqlite3
stimulus-rails
thruster
turbo-rails!

View File

@@ -17,7 +17,7 @@ class AccountsController < ApplicationController
end
def account_params
params.require(:account).permit(:name, :logo)
params.require(:account).permit(:name, :logo, settings: {})
end
def account_users

View File

@@ -3,6 +3,7 @@ class Rooms::ClosedsController < RoomsController
before_action :ensure_can_administer, only: %i[ update ]
before_action :remember_last_room_visited, only: :show
before_action :force_room_type, only: %i[ edit update ]
before_action :ensure_permission_to_create_rooms, only: %i[ new create ]
DEFAULT_ROOM_NAME = "New room"

View File

@@ -3,6 +3,7 @@ class Rooms::OpensController < RoomsController
before_action :ensure_can_administer, only: %i[ update ]
before_action :remember_last_room_visited, only: :show
before_action :force_room_type, only: %i[ edit update ]
before_action :ensure_permission_to_create_rooms, only: %i[ new create ]
DEFAULT_ROOM_NAME = "New room"

View File

@@ -31,6 +31,12 @@ class RoomsController < ApplicationController
head :forbidden unless Current.user.can_administer?(@room)
end
def ensure_permission_to_create_rooms
if Current.account.settings.restrict_room_creation_to_administrators? && !Current.user.administrator?
head :forbidden
end
end
def find_messages
messages = @room.messages.with_creator.with_attachment_details.with_boosts

View File

@@ -2,4 +2,5 @@ class Account < ApplicationRecord
include Joinable
has_one_attached :logo
has_json :settings, restrict_room_creation_to_administrators: false
end

View File

@@ -53,7 +53,7 @@ class Webhook < ApplicationRecord
end
def extract_text_from(response)
response.body.dup.force_encoding("UTF-8") if response.code == "200" && response.content_type.in?(%w[ text/html text/plain ])
String.new(response.body).force_encoding("UTF-8") if response.code == "200" && response.content_type.in?(%w[ text/html text/plain ])
end
def receive_text_reply_to(room, text:)

View File

@@ -64,6 +64,30 @@
<% end %>
</div>
<% end %>
<div class="margin-block-start pad-block pad-inline-double fill-shade border-radius">
<%= form_with model: @account, method: :put, data: { controller: "form" }, class: "flex align-center gap center" do |form| %>
<div class="flex-item-grow flex align-center gap txt-align-start">
<%= image_tag "crown.svg", class: "colorize--black", aria: { hidden: "true" }, size: 18 %> Must be admin to create new rooms
</div>
<%= form.fields_for :settings, @account.settings do |settings_form| %>
<%= settings_form.hidden_field :restrict_room_creation_to_administrators,
value: !Current.account.settings.restrict_room_creation_to_administrators? %>
<% end %>
<label class="switch">
<input type="checkbox"
class="switch__input"
<%= "checked" if Current.account.settings.restrict_room_creation_to_administrators? %>
data-action="change->form#submit">
<span class="switch__btn round"></span>
<span class="for-screen-reader">
Must be admin to create new rooms
</span>
</label>
<% end %>
</div>
<% else %>
<%= account_logo_tag style: "txt-xx-large center" %>
<h1 class="flex-item-grow txt-x-large"><%= @account.name %></h1>

View File

@@ -36,8 +36,10 @@
<% end %>
</div>
<%= link_to new_rooms_open_path, class: "rooms__new-btn btn room align-center gap txt-reversed", aria: { label: "New Chat Room" } do %>
<%= image_tag "add.svg", size: 20, aria: { hidden: "true" }, style: "view-transition-name: new-room" %>
<% if Current.user.administrator? || !Current.account.settings.restrict_room_creation_to_administrators? %>
<%= link_to new_rooms_open_path, class: "rooms__new-btn btn room align-center gap txt-reversed", aria: { label: "New Chat Room" } do %>
<%= image_tag "add.svg", size: 20, aria: { hidden: "true" }, style: "view-transition-name: new-room" %>
<% end %>
<% end %>
</div>

View File

@@ -0,0 +1,5 @@
class AddAccountSettings < ActiveRecord::Migration[7.2]
def change
add_column :accounts, :settings, :json
end
end

3
db/schema.rb generated
View File

@@ -10,12 +10,13 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.1].define(version: 2025_11_26_115722) do
ActiveRecord::Schema[8.2].define(version: 2025_11_26_130131) do
create_table "accounts", force: :cascade do |t|
t.datetime "created_at", null: false
t.text "custom_styles"
t.string "join_code", null: false
t.string "name", null: false
t.json "settings"
t.datetime "updated_at", null: false
end

View File

@@ -29,6 +29,16 @@ class Rooms::ClosedsControllerTest < ActionDispatch::IntegrationTest
assert_redirected_to room_url(Room.last)
end
test "create forbidden by non-admin when account restricts creation to admins" do
accounts(:signal).settings.restrict_room_creation_to_administrators = true
accounts(:signal).save!
sign_in :jz
post rooms_closeds_url, params: { room: { name: "My New Room" }, user_ids: [ users(:david).id, users(:kevin).id, users(:jason).id ] }
assert_response :forbidden
end
test "update with membership revisions" do
assert_difference -> { rooms(:designers).reload.users.count }, -1 do
put rooms_closed_url(rooms(:designers)), params: {

View File

@@ -24,6 +24,15 @@ class Rooms::OpensControllerTest < ActionDispatch::IntegrationTest
assert_redirected_to room_url(Room.last)
end
test "create forbidden by non-admin when account restricts creation to admins" do
accounts(:signal).settings.restrict_room_creation_to_administrators = true
accounts(:signal).save!
sign_in :jz
post rooms_opens_url, params: { room: { name: "My New Room" } }
assert_response :forbidden
end
test "only admins or creators can update" do
sign_in :jz

View File

@@ -1,4 +1,18 @@
require "test_helper"
class AccountTest < ActiveSupport::TestCase
test "settings" do
accounts(:signal).settings.restrict_room_creation_to_administrators = true
assert accounts(:signal).settings.restrict_room_creation_to_administrators?
assert_equal({ "restrict_room_creation_to_administrators" => true }, accounts(:signal)[:settings])
accounts(:signal).update!(settings: { "restrict_room_creation_to_administrators" => "true" })
assert accounts(:signal).reload.settings.restrict_room_creation_to_administrators?
accounts(:signal).settings.restrict_room_creation_to_administrators = false
assert_not accounts(:signal).settings.restrict_room_creation_to_administrators?
assert_equal({ "restrict_room_creation_to_administrators" => false }, accounts(:signal)[:settings])
accounts(:signal).update!(settings: { "restrict_room_creation_to_administrators" => "false" })
assert_not accounts(:signal).reload.settings.restrict_room_creation_to_administrators?
end
end

View File

@@ -5,6 +5,7 @@ require "rails/test_help"
require "minitest/unit"
require "mocha/minitest"
require "webmock/minitest"
require "turbo/broadcastable/test_helper"
WebMock.enable!