mirror of
https://github.com/mastodon/mastodon.git
synced 2026-03-21 18:05:23 -05:00
Split invite_users permission into invite_bypass_approval (#38278)
Some checks are pending
Bundler Audit / security (push) Waiting to run
Check i18n / check-i18n (push) Waiting to run
Chromatic / Check for relevant changes (push) Waiting to run
Chromatic / Run Chromatic (push) Blocked by required conditions
CodeQL / Analyze (actions) (push) Waiting to run
CodeQL / Analyze (javascript) (push) Waiting to run
CodeQL / Analyze (ruby) (push) Waiting to run
Crowdin / Upload translations / upload-translations (push) Waiting to run
Check formatting / lint (push) Waiting to run
CSS Linting / lint (push) Waiting to run
Haml Linting / lint (push) Waiting to run
JavaScript Linting / lint (push) Waiting to run
Ruby Linting / lint (push) Waiting to run
JavaScript Testing / test (push) Waiting to run
Historical data migration test / test (14-alpine) (push) Waiting to run
Historical data migration test / test (15-alpine) (push) Waiting to run
Historical data migration test / test (16-alpine) (push) Waiting to run
Historical data migration test / test (17-alpine) (push) Waiting to run
Ruby Testing / build (production) (push) Waiting to run
Ruby Testing / build (test) (push) Waiting to run
Ruby Testing / test (.ruby-version) (push) Blocked by required conditions
Ruby Testing / test (3.2) (push) Blocked by required conditions
Ruby Testing / test (3.3) (push) Blocked by required conditions
Ruby Testing / End to End testing (.ruby-version) (push) Blocked by required conditions
Ruby Testing / End to End testing (3.2) (push) Blocked by required conditions
Ruby Testing / End to End testing (3.3) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:7.17.29) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:8.19.2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, opensearchproject/opensearch:2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (3.2, docker.elastic.co/elasticsearch/elasticsearch:7.17.29) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (3.3, docker.elastic.co/elasticsearch/elasticsearch:7.17.29) (push) Blocked by required conditions
Some checks are pending
Bundler Audit / security (push) Waiting to run
Check i18n / check-i18n (push) Waiting to run
Chromatic / Check for relevant changes (push) Waiting to run
Chromatic / Run Chromatic (push) Blocked by required conditions
CodeQL / Analyze (actions) (push) Waiting to run
CodeQL / Analyze (javascript) (push) Waiting to run
CodeQL / Analyze (ruby) (push) Waiting to run
Crowdin / Upload translations / upload-translations (push) Waiting to run
Check formatting / lint (push) Waiting to run
CSS Linting / lint (push) Waiting to run
Haml Linting / lint (push) Waiting to run
JavaScript Linting / lint (push) Waiting to run
Ruby Linting / lint (push) Waiting to run
JavaScript Testing / test (push) Waiting to run
Historical data migration test / test (14-alpine) (push) Waiting to run
Historical data migration test / test (15-alpine) (push) Waiting to run
Historical data migration test / test (16-alpine) (push) Waiting to run
Historical data migration test / test (17-alpine) (push) Waiting to run
Ruby Testing / build (production) (push) Waiting to run
Ruby Testing / build (test) (push) Waiting to run
Ruby Testing / test (.ruby-version) (push) Blocked by required conditions
Ruby Testing / test (3.2) (push) Blocked by required conditions
Ruby Testing / test (3.3) (push) Blocked by required conditions
Ruby Testing / End to End testing (.ruby-version) (push) Blocked by required conditions
Ruby Testing / End to End testing (3.2) (push) Blocked by required conditions
Ruby Testing / End to End testing (3.3) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:7.17.29) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:8.19.2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, opensearchproject/opensearch:2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (3.2, docker.elastic.co/elasticsearch/elasticsearch:7.17.29) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (3.3, docker.elastic.co/elasticsearch/elasticsearch:7.17.29) (push) Blocked by required conditions
This commit is contained in:
parent
49430b7eea
commit
1ee457f2d3
|
|
@ -166,6 +166,38 @@ on('change', '#domain_block_severity', ({ target }) => {
|
|||
if (target instanceof HTMLSelectElement) onDomainBlockSeverityChange(target);
|
||||
});
|
||||
|
||||
const onChangeInviteUsersPermission = (target: HTMLInputElement) => {
|
||||
const inviteBypassApprovalCheckbox = document.querySelector<HTMLInputElement>(
|
||||
'input#user_role_permissions_as_keys_invite_bypass_approval',
|
||||
);
|
||||
|
||||
if (inviteBypassApprovalCheckbox) {
|
||||
inviteBypassApprovalCheckbox.disabled = !target.checked;
|
||||
|
||||
if (target.checked) {
|
||||
inviteBypassApprovalCheckbox.parentElement?.classList.remove('disabled');
|
||||
inviteBypassApprovalCheckbox.parentElement?.parentElement?.classList.remove(
|
||||
'disabled',
|
||||
);
|
||||
} else {
|
||||
inviteBypassApprovalCheckbox.parentElement?.classList.add('disabled');
|
||||
inviteBypassApprovalCheckbox.parentElement?.parentElement?.classList.add(
|
||||
'disabled',
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
on(
|
||||
'change',
|
||||
'input#user_role_permissions_as_keys_invite_users',
|
||||
({ target }) => {
|
||||
if (target instanceof HTMLInputElement) {
|
||||
onChangeInviteUsersPermission(target);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
function onEnableBootstrapTimelineAccountsChange(target: HTMLInputElement) {
|
||||
const bootstrapTimelineAccountsField =
|
||||
document.querySelector<HTMLInputElement>(
|
||||
|
|
@ -291,6 +323,13 @@ ready(() => {
|
|||
);
|
||||
if (registrationMode) onChangeRegistrationMode(registrationMode);
|
||||
|
||||
const inviteUsersPermissionChecbkox =
|
||||
document.querySelector<HTMLInputElement>(
|
||||
'input#user_role_permissions_as_keys_invite_users',
|
||||
);
|
||||
if (inviteUsersPermissionChecbkox)
|
||||
onChangeInviteUsersPermission(inviteUsersPermissionChecbkox);
|
||||
|
||||
const checkAllElement = document.querySelector<HTMLInputElement>(
|
||||
'#batch_checkbox_all',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -506,6 +506,10 @@ code {
|
|||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox.disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
label.checkbox {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class Invite < ApplicationRecord
|
|||
COMMENT_SIZE_LIMIT = 420
|
||||
ELIGIBLE_CODE_CHARACTERS = [*('a'..'z'), *('A'..'Z'), *('0'..'9')].freeze
|
||||
HOMOGLYPHS = %w(0 1 I l O).freeze
|
||||
VALID_CODE_CHARACTERS = ELIGIBLE_CODE_CHARACTERS - HOMOGLYPHS
|
||||
VALID_CODE_CHARACTERS = (ELIGIBLE_CODE_CHARACTERS - HOMOGLYPHS).freeze
|
||||
|
||||
belongs_to :user, inverse_of: :invites
|
||||
has_many :users, inverse_of: :invite, dependent: nil
|
||||
|
|
@ -37,6 +37,10 @@ class Invite < ApplicationRecord
|
|||
(max_uses.nil? || uses < max_uses) && !expired? && user&.functional?
|
||||
end
|
||||
|
||||
def bypass_approval?
|
||||
user&.role&.can?(:invite_bypass_approval)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_code
|
||||
|
|
|
|||
|
|
@ -168,6 +168,10 @@ class User < ApplicationRecord
|
|||
invite_id.present? && invite.valid_for_use?
|
||||
end
|
||||
|
||||
def valid_bypassing_invitation?
|
||||
valid_invitation? && invite.bypass_approval?
|
||||
end
|
||||
|
||||
def disable!
|
||||
update!(disabled: true)
|
||||
|
||||
|
|
@ -420,7 +424,7 @@ class User < ApplicationRecord
|
|||
if requires_approval?
|
||||
false
|
||||
else
|
||||
open_registrations? || valid_invitation? || external?
|
||||
open_registrations? || valid_bypassing_invitation? || external?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ class UserRole < ApplicationRecord
|
|||
manage_user_access: (1 << 18),
|
||||
delete_user_data: (1 << 19),
|
||||
view_feeds: (1 << 20),
|
||||
invite_bypass_approval: (1 << 21),
|
||||
}.freeze
|
||||
|
||||
EVERYONE_ROLE_ID = -99
|
||||
|
|
@ -51,10 +52,12 @@ class UserRole < ApplicationRecord
|
|||
ALL = FLAGS.values.reduce(&:|)
|
||||
|
||||
DEFAULT = FLAGS[:invite_users]
|
||||
SAFE = FLAGS[:invite_users] | FLAGS[:invite_bypass_approval]
|
||||
|
||||
CATEGORIES = {
|
||||
invites: %i(
|
||||
invite_users
|
||||
invite_bypass_approval
|
||||
).freeze,
|
||||
|
||||
moderation: %i(
|
||||
|
|
@ -206,6 +209,6 @@ class UserRole < ApplicationRecord
|
|||
end
|
||||
|
||||
def validate_dangerous_permissions
|
||||
errors.add(:permissions_as_keys, :dangerous) if everyone? && Flags::DEFAULT & permissions != permissions
|
||||
errors.add(:permissions_as_keys, :dangerous) if everyone? && Flags::SAFE & permissions != permissions
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -778,6 +778,8 @@ en:
|
|||
administrator_description: Users with this permission will bypass every permission
|
||||
delete_user_data: Delete User Data
|
||||
delete_user_data_description: Allows users to delete other users' data without delay
|
||||
invite_bypass_approval: Invite Users without review
|
||||
invite_bypass_approval_description: Allows people invited to the server by these users to bypass moderation approval
|
||||
invite_users: Invite Users
|
||||
invite_users_description: Allows users to invite new people to the server
|
||||
manage_announcements: Manage Announcements
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddInviteApprovalBypassPermission < ActiveRecord::Migration[8.1]
|
||||
class UserRole < ApplicationRecord; end
|
||||
|
||||
def up
|
||||
UserRole.where('permissions & (1 << 16) = 1 << 16').update_all('permissions = permissions | (1 << 21)')
|
||||
end
|
||||
|
||||
def down; end
|
||||
end
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[8.1].define(version: 2026_03_11_152331) do
|
||||
ActiveRecord::Schema[8.1].define(version: 2026_03_18_144837) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pg_catalog.plpgsql"
|
||||
|
||||
|
|
|
|||
|
|
@ -298,7 +298,6 @@ RSpec.describe Auth::RegistrationsController do
|
|||
|
||||
context 'with Approval-based registrations with valid invite and required invite text' do
|
||||
subject do
|
||||
inviter = Fabricate(:user, confirmed_at: 2.days.ago)
|
||||
Setting.registrations_mode = 'approved'
|
||||
Setting.require_invite_text = true
|
||||
request.headers['Accept-Language'] = accept_language
|
||||
|
|
@ -306,7 +305,9 @@ RSpec.describe Auth::RegistrationsController do
|
|||
post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', invite_code: invite.code, agreement: 'true' } }
|
||||
end
|
||||
|
||||
it 'redirects to setup and creates user' do
|
||||
let!(:inviter) { Fabricate(:user, confirmed_at: 2.days.ago) }
|
||||
|
||||
it 'redirects to setup and creates user in a non-approved state' do
|
||||
subject
|
||||
|
||||
expect(response).to redirect_to auth_setup_path
|
||||
|
|
@ -315,9 +316,28 @@ RSpec.describe Auth::RegistrationsController do
|
|||
.to be_present
|
||||
.and have_attributes(
|
||||
locale: eq(accept_language),
|
||||
approved: be(true)
|
||||
approved: be(false)
|
||||
)
|
||||
end
|
||||
|
||||
context 'when the inviting user has the permission to bypass approval' do
|
||||
before do
|
||||
inviter.role.update!(permissions: inviter.role.permissions | UserRole::FLAGS[:invite_bypass_approval])
|
||||
end
|
||||
|
||||
it 'redirects to setup and creates user in an approved state' do
|
||||
subject
|
||||
|
||||
expect(response).to redirect_to auth_setup_path
|
||||
|
||||
expect(User.find_by(email: 'test@example.com'))
|
||||
.to be_present
|
||||
.and have_attributes(
|
||||
locale: eq(accept_language),
|
||||
approved: be(true)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an already taken username' do
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user