2
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2025-10-03 17:21:20 +08:00

FEATURE: Add site setting to prevent mods from changing trust levels

This commit is contained in:
OsamaSayegh 2025-10-03 02:22:32 +03:00
parent 2b7835d02a
commit 6128a0736d
No known key found for this signature in database
GPG key ID: 060E5AC82223685F
9 changed files with 106 additions and 28 deletions

View file

@ -2,7 +2,7 @@ import { fn, hash } from "@ember/helper";
import { LinkTo } from "@ember/routing";
import { htmlSafe } from "@ember/template";
import RouteTemplate from "ember-route-template";
import { and, gt } from "truth-helpers";
import { and, gt, not } from "truth-helpers";
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
import DButton from "discourse/components/d-button";
import PluginOutlet from "discourse/components/plugin-outlet";
@ -476,10 +476,14 @@ export default RouteTemplate(
<div class="field">{{i18n "trust_level"}}</div>
<div class="value">
<ComboBox
class="change-trust-level-dropdown"
@content={{@controller.site.trustLevels}}
@nameProperty="detailedName"
@value={{@controller.model.trustLevel.id}}
@onChange={{fn (mut @controller.model.trust_level)}}
@options={{hash
disabled=(not @controller.model.can_change_trust_level)
}}
/>

{{#if @controller.model.dirty}}
@ -498,31 +502,33 @@ export default RouteTemplate(
{{/if}}
</div>
<div class="controls">
{{#if @controller.model.canLockTrustLevel}}
{{#if @controller.hasLockedTrustLevel}}
{{icon "lock" title="admin.user.trust_level_locked_tip"}}
<DButton
@action={{fn @controller.lockTrustLevel false}}
@label="admin.user.unlock_trust_level"
class="btn-default"
/>
{{else}}
{{icon "unlock" title="admin.user.trust_level_unlocked_tip"}}
<DButton
@action={{fn @controller.lockTrustLevel true}}
@label="admin.user.lock_trust_level"
class="btn-default"
/>
{{#if @controller.model.can_change_trust_level}}
{{#if @controller.model.canLockTrustLevel}}
{{#if @controller.hasLockedTrustLevel}}
{{icon "lock" title="admin.user.trust_level_locked_tip"}}
<DButton
@action={{fn @controller.lockTrustLevel false}}
@label="admin.user.unlock_trust_level"
class="btn-default"
/>
{{else}}
{{icon "unlock" title="admin.user.trust_level_unlocked_tip"}}
<DButton
@action={{fn @controller.lockTrustLevel true}}
@label="admin.user.lock_trust_level"
class="btn-default"
/>
{{/if}}
{{/if}}
{{#if @controller.model.tl3Requirements}}
<LinkTo
@route="adminUser.tl3Requirements"
@model={{@controller.model}}
class="btn btn-default"
>
{{i18n "admin.user.trust_level_3_requirements"}}
</LinkTo>
{{/if}}
{{/if}}
{{#if @controller.model.tl3Requirements}}
<LinkTo
@route="adminUser.tl3Requirements"
@model={{@controller.model}}
class="btn btn-default"
>
{{i18n "admin.user.trust_level_3_requirements"}}
</LinkTo>
{{/if}}
</div>
</div>

View file

@ -7,6 +7,7 @@ class AdminUserSerializer < AdminUserListSerializer
:can_activate,
:can_deactivate,
:can_approve,
:can_change_trust_level,
:ip_address,
:registration_ip_address,
:include_ip
@ -33,6 +34,10 @@ class AdminUserSerializer < AdminUserListSerializer
scope.can_deactivate?(object)
end

def can_change_trust_level
scope.can_change_trust_level?(object)
end

def ip_address
object.ip_address.try(:to_s)
end

View file

@ -1931,6 +1931,7 @@ en:
top_page_default_timeframe: "Default top page time period for anonymous users (automatically adjusts for logged in users based on their last visit)."
moderators_view_emails: "Allow moderators to view user email addresses."
moderators_view_ips: "Allow moderators to view user ip addresses."
moderators_change_trust_levels: "Allow moderators to change user trust levels."
prioritize_username_in_ux: "Show username first on user page, user card and posts (when disabled name is shown first)"
enable_rich_text_paste: "Enable automatic HTML to Markdown conversion when pasting text into the composer."
send_old_credential_reminder_days: "Remind about old credentials after days"

View file

@ -2588,6 +2588,8 @@ security:
moderators_view_ips:
default: true
client: true
moderators_change_trust_levels:
default: true
non_crawler_user_agents:
hidden: true
default: "trident|webkit|gecko|chrome|safari|msie|opera|goanna|discourse"

View file

@ -397,7 +397,7 @@ class Guardian
end

def can_change_trust_level?(user)
user && is_staff?
user && (is_admin? || (is_moderator? && SiteSetting.moderators_change_trust_levels))
end

# Support sites that have to approve users

View file

@ -1038,7 +1038,17 @@ RSpec.describe Admin::UsersController do

before { sign_in(admin) }

include_examples "trust level updates possible"
context "when moderators_change_trust_levels setting is enabled" do
before { SiteSetting.moderators_change_trust_levels = true }

include_examples "trust level updates possible"
end

context "when moderators_change_trust_levels setting is disabled" do
before { SiteSetting.moderators_change_trust_levels = false }

include_examples "trust level updates possible"
end
end

context "when logged in as a moderator" do
@ -1046,7 +1056,23 @@ RSpec.describe Admin::UsersController do

before { sign_in(moderator) }

include_examples "trust level updates possible"
context "when moderators_change_trust_levels setting is enabled" do
before { SiteSetting.moderators_change_trust_levels = true }

include_examples "trust level updates possible"
end

context "when moderators_change_trust_levels setting is disabled" do
before { SiteSetting.moderators_change_trust_levels = false }

it "prevents updates to trust level with a 422 response" do
another_user.update(trust_level: TrustLevel[1])
put "/admin/users/#{another_user.id}/trust_level.json", params: { level: TrustLevel[0] }

expect(response.status).to eq(422)
expect(another_user.reload.trust_level).to eq(TrustLevel[1])
end
end
end

context "when logged in as a non-staff user" do

View file

@ -80,6 +80,9 @@
"can_deactivate": {
"type": "boolean"
},
"can_change_trust_level": {
"type": "boolean"
},
"ip_address": {
"type": "string"
},

View file

@ -125,4 +125,30 @@ describe "Admin User Page", type: :system do
end
end
end

context "when logged in as a moderator" do
fab!(:current_user, :moderator)

context "when visiting a regular user's page" do
fab!(:user)

before { admin_user_page.visit(user) }

context "when moderators_change_trust_levels setting is enabled" do
before { SiteSetting.moderators_change_trust_levels = true }

it "the dropdown to change trust level is enabled" do
expect(admin_user_page).to have_change_trust_level_dropdown_enabled
end
end

context "when moderators_change_trust_levels setting is disabled" do
before { SiteSetting.moderators_change_trust_levels = false }

it "the dropdown to change trust level is disabled" do
expect(admin_user_page).to have_change_trust_level_dropdown_disabled
end
end
end
end
end

View file

@ -23,6 +23,15 @@ module PageObjects
has_no_css?(".btn-danger.silence-user")
end

def has_change_trust_level_dropdown_enabled?
has_css?(".change-trust-level-dropdown") &&
has_no_css?(".change-trust-level-dropdown.is-disabled")
end

def has_change_trust_level_dropdown_disabled?
has_css?(".change-trust-level-dropdown.is-disabled")
end

def click_suspend_button
find(".btn-danger.suspend-user").click
end