[Security] Implement read only mode (#821)

This commit is contained in:
Cinder 2024-12-08 19:28:31 -08:00 committed by GitHub
parent 5c345528e4
commit 4883912e93
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 438 additions and 170 deletions

View File

@ -346,7 +346,6 @@ Layout/SpaceAfterComma:
- 'app/logical/storage_manager/local.rb'
- 'app/presenters/post_presenter.rb'
- 'app/serializers/post_serializer.rb'
- 'app/views/admin/danger_zone/index.html.erb'
- 'app/views/mod_actions/_search.html.erb'
- 'app/views/post_events/_search.html.erb'
- 'app/views/post_events/index.html.erb'
@ -475,7 +474,6 @@ Layout/SpaceInsideBlockBraces:
- 'app/models/wiki_page.rb'
- 'app/presenters/tag_set_presenter.rb'
- 'app/presenters/user_presenter.rb'
- 'app/views/admin/danger_zone/index.html.erb'
- 'app/views/comments/_index_by_post.html.erb'
- 'app/views/forum_topics/index.html.erb'
- 'app/views/mod_actions/_search.html.erb'
@ -877,7 +875,6 @@ Lint/UnusedBlockArgument:
- 'app/controllers/posts_controller.rb'
- 'app/indexes/post_index.rb'
- 'app/models/post.rb'
- 'app/views/admin/danger_zone/index.html.erb'
- 'config/application.rb'
- 'config/initializers/content_security_policy.rb'
- 'config/routes.rb'
@ -1483,7 +1480,6 @@ Style/ClassAndModuleChildren:
- 'app/logical/storage_manager/local.rb'
- 'app/logical/storage_manager/match.rb'
- 'app/logical/storage_manager/null.rb'
- 'test/functional/admin/danger_zone_controller_test.rb'
- 'test/functional/admin/dashboards_controller_test.rb'
- 'test/functional/admin/users_controller_test.rb'
- 'test/test_helper.rb'

View File

@ -1,29 +0,0 @@
# frozen_string_literal: true
module Admin
class DangerZoneController < ApplicationController
before_action :admin_only
def index
end
def uploading_limits
new_level = params[:uploading_limits][:min_level].to_i
raise ArgumentError, "#{new_level} is not valid" unless User.level_hash.values.include? new_level
if new_level != DangerZone.min_upload_level
DangerZone.min_upload_level = new_level
StaffAuditLog.log(:min_upload_level, CurrentUser.user, { level: new_level })
end
redirect_to admin_danger_zone_index_path
end
def hide_pending_posts
duration = params[:hide_pending_posts][:duration].to_f
if duration >= 0 && duration != DangerZone.hide_pending_posts_for
DangerZone.hide_pending_posts_for = duration
StaffAuditLog.log(:hide_pending_posts_for, CurrentUser.user, { duration: duration })
end
redirect_to admin_danger_zone_index_path
end
end
end

View File

@ -3,9 +3,10 @@
class BlipsController < ApplicationController
class BlipTooOld < Exception ; end
respond_to :html, :json
before_action :member_only, only: [:create, :new, :update, :edit, :hide]
before_action :moderator_only, only: [:unhide, :warning]
before_action :member_only, only: %i[create new update edit hide]
before_action :moderator_only, only: %i[unhide warning]
before_action :admin_only, only: [:destroy]
before_action :ensure_lockdown_disabled, except: %i[index show]
rescue_from BlipTooOld, with: :blip_too_old
@ -123,4 +124,8 @@ class BlipsController < ApplicationController
raise BlipTooOld if blip.created_at < 5.minutes.ago && !CurrentUser.is_admin?
raise User::PrivilegeError unless blip.can_edit?(CurrentUser.user)
end
def ensure_lockdown_disabled
access_denied if Security::Lockdown.blips_disabled? && !CurrentUser.is_staff?
end
end

View File

@ -2,9 +2,10 @@
class BulkUpdateRequestsController < ApplicationController
respond_to :html, :json
before_action :member_only, except: [:index, :show]
before_action :member_only, except: %i[index show]
before_action :admin_only, only: [:approve]
before_action :load_bulk_update_request, except: [:new, :create, :index]
before_action :load_bulk_update_request, except: %i[new create index]
before_action :ensure_lockdown_disabled, except: %i[index show]
def new
@bulk_update_request = BulkUpdateRequest.new
@ -74,4 +75,8 @@ class BulkUpdateRequestsController < ApplicationController
params.require(:bulk_update_request).permit(permitted_params)
end
def ensure_lockdown_disabled
access_denied if Security::Lockdown.aiburs_disabled? && !CurrentUser.is_staff?
end
end

View File

@ -4,8 +4,9 @@ class CommentVotesController < ApplicationController
respond_to :json
respond_to :html, only: [:index]
before_action :member_only
before_action :moderator_only, only: [:index, :lock]
before_action :moderator_only, only: %i[index lock]
before_action :admin_only, only: [:delete]
before_action :ensure_lockdown_disabled
skip_before_action :api_check
def create
@ -54,4 +55,8 @@ class CommentVotesController < ApplicationController
permitted_params += %i[user_ip_addr duplicates_only order] if CurrentUser.is_admin?
permit_search_params permitted_params
end
def ensure_lockdown_disabled
access_denied if Security::Lockdown.votes_disabled? && !CurrentUser.is_staff?
end
end

View File

@ -3,8 +3,9 @@
class CommentsController < ApplicationController
respond_to :html, :json
before_action :member_only, except: %i[index search show for_post]
before_action :moderator_only, only: [:unhide, :warning]
before_action :admin_only, only: [:destroy]
before_action :moderator_only, only: %i[unhide warning]
before_action :admin_only, only: %i[destroy]
before_action :ensure_lockdown_disabled, except: %i[index search show for_post]
skip_before_action :api_check
def index
@ -140,4 +141,8 @@ private
params.fetch(:comment, {}).permit(permitted_params)
end
def ensure_lockdown_disabled
access_denied if Security::Lockdown.comments_disabled? && !CurrentUser.is_staff?
end
end

View File

@ -2,6 +2,7 @@
class FavoritesController < ApplicationController
before_action :member_only, except: [:index]
before_action :ensure_lockdown_disabled, except: %i[index]
respond_to :html, :json
skip_before_action :api_check
@ -44,4 +45,8 @@ class FavoritesController < ApplicationController
rescue Favorite::Error => x
render_expected_error(422, x.message)
end
def ensure_lockdown_disabled
access_denied if Security::Lockdown.favorites_disabled? && !CurrentUser.is_staff?
end
end

View File

@ -7,6 +7,7 @@ class ForumPostVotesController < ApplicationController
before_action :validate_forum_post
before_action :validate_no_vote_on_own_post, only: [:create]
before_action :load_vote, only: [:destroy]
before_action :ensure_lockdown_disabled
def create
@forum_post_vote = @forum_post.votes.create(forum_post_vote_params)
@ -46,4 +47,8 @@ private
def forum_post_vote_params
params.fetch(:forum_post_vote, {}).permit(:score)
end
def ensure_lockdown_disabled
access_denied if Security::Lockdown.votes_disabled? && !CurrentUser.is_staff?
end
end

View File

@ -2,11 +2,12 @@
class ForumPostsController < ApplicationController
respond_to :html, :json
before_action :member_only, :except => [:index, :show, :search]
before_action :moderator_only, only: [:unhide, :warning]
before_action :member_only, except: %i[index show search]
before_action :moderator_only, only: %i[unhide warning]
before_action :admin_only, only: [:destroy]
before_action :load_post, :only => [:edit, :show, :update, :destroy, :hide, :unhide, :warning]
before_action :check_min_level, :only => [:edit, :show, :update, :destroy, :hide, :unhide]
before_action :load_post, only: %i[edit show update destroy hide unhide warning]
before_action :check_min_level, only: %i[edit show update destroy hide unhide]
before_action :ensure_lockdown_disabled, except: %i[index show search]
skip_before_action :api_check
def new
@ -109,4 +110,8 @@ class ForumPostsController < ApplicationController
params.fetch(:forum_post, {}).permit(permitted_params)
end
def ensure_lockdown_disabled
access_denied if Security::Lockdown.forums_disabled? && !CurrentUser.is_staff?
end
end

View File

@ -2,12 +2,13 @@
class ForumTopicsController < ApplicationController
respond_to :html, :json
before_action :member_only, :except => [:index, :show]
before_action :moderator_only, :only => [:unhide]
before_action :member_only, except: %i[index show]
before_action :moderator_only, only: [:unhide]
before_action :admin_only, only: [:destroy]
before_action :normalize_search, :only => :index
before_action :load_topic, :only => [:edit, :show, :update, :destroy, :hide, :unhide, :subscribe, :unsubscribe]
before_action :check_min_level, :only => [:show, :edit, :update, :destroy, :hide, :unhide, :subscribe, :unsubscribe]
before_action :normalize_search, only: :index
before_action :load_topic, only: %i[edit show update destroy hide unhide subscribe unsubscribe]
before_action :check_min_level, only: %i[show edit update destroy hide unhide subscribe unsubscribe]
before_action :ensure_lockdown_disabled, except: %i[index show]
skip_before_action :api_check
def new
@ -143,4 +144,8 @@ private
params.fetch(:forum_topic, {}).permit(permitted_params)
end
def ensure_lockdown_disabled
access_denied if Security::Lockdown.forums_disabled? && !CurrentUser.is_staff?
end
end

View File

@ -2,8 +2,9 @@
class PoolsController < ApplicationController
respond_to :html, :json
before_action :member_only, :except => [:index, :show, :gallery]
before_action :janitor_only, :only => [:destroy]
before_action :member_only, except: %i[index show gallery]
before_action :janitor_only, only: %i[destroy]
before_action :ensure_lockdown_disabled, except: %i[index show gallery]
def new
@pool = Pool.new
@ -82,4 +83,8 @@ class PoolsController < ApplicationController
permitted_params = %i[name description category is_active post_ids post_ids_string]
params.require(:pool).permit(*permitted_params, post_ids: [])
end
def ensure_lockdown_disabled
access_denied if Security::Lockdown.pools_disabled? && !CurrentUser.is_staff?
end
end

View File

@ -109,8 +109,6 @@ class PostReplacementsController < ApplicationController
end
def ensure_uploads_enabled
if DangerZone.uploads_disabled?(CurrentUser.user)
access_denied "Uploads are disabled"
end
access_denied if Security::Lockdown.uploads_disabled? || CurrentUser.user.level < Security::Lockdown.uploads_min_level
end
end

View File

@ -2,7 +2,8 @@
class PostSetsController < ApplicationController
respond_to :html, :json
before_action :member_only, except: [:index, :show]
before_action :member_only, except: %i[index show]
before_action :ensure_lockdown_disabled, except: %i[index show]
def index
if !params[:post_id].blank?
@ -161,4 +162,8 @@ class PostSetsController < ApplicationController
permitted_params += %i[is_public] if CurrentUser.is_moderator?
permit_search_params permitted_params
end
def ensure_lockdown_disabled
access_denied if Security::Lockdown.post_sets_disabled? && !CurrentUser.is_staff?
end
end

View File

@ -2,8 +2,9 @@
class PostVotesController < ApplicationController
before_action :member_only
before_action :moderator_only, only: [:index, :lock]
before_action :moderator_only, only: %i[index lock]
before_action :admin_only, only: [:delete]
before_action :ensure_lockdown_disabled
skip_before_action :api_check
def create
@ -51,4 +52,8 @@ class PostVotesController < ApplicationController
permitted_params += %i[user_ip_addr duplicates_only order] if CurrentUser.is_admin?
permit_search_params permitted_params
end
def ensure_lockdown_disabled
access_denied if Security::Lockdown.votes_disabled? && !CurrentUser.is_staff?
end
end

View File

@ -3,6 +3,7 @@
class PostsController < ApplicationController
before_action :member_only, except: %i[show show_seq index random]
before_action :admin_only, only: [:update_iqdb]
before_action :ensure_lockdown_disabled, except: %i[index show show_seq random]
respond_to :html, :json
def index
@ -26,7 +27,7 @@ class PostsController < ApplicationController
def show
@post = Post.find(params[:id])
raise User::PrivilegeError.new("Post unavailable") unless DangerZone.post_visible?(@post, CurrentUser.user)
raise User::PrivilegeError, "Post unavailable" unless Security::Lockdown.post_visible?(@post, CurrentUser.user)
include_deleted = @post.is_deleted? || (@post.parent_id.present? && @post.parent.is_deleted?) || CurrentUser.is_approver?
@parent_post_set = PostSets::PostRelationship.new(@post.parent_id, :include_deleted => include_deleted, want_parent: true)
@ -164,6 +165,10 @@ class PostsController < ApplicationController
raise User::PrivilegeError.new("Updater #{User.throttle_reason(can_edit)}") unless can_edit == true
end
def ensure_lockdown_disabled
access_denied if Security::Lockdown.uploads_disabled? && !CurrentUser.is_staff?
end
def post_params
permitted_params = %i[
tag_string old_tag_string

View File

@ -0,0 +1,11 @@
# frozen_string_literal: true
module Security
class DashboardController < ApplicationController
respond_to :html
before_action :admin_only
def index
end
end
end

View File

@ -0,0 +1,71 @@
# frozen_string_literal: true
module Security
class LockdownController < ApplicationController
before_action :admin_only
def index
end
def panic
Security::Lockdown.uploads_disabled = "1"
Security::Lockdown.pools_disabled = "1"
Security::Lockdown.post_sets_disabled = "1"
Security::Lockdown.comments_disabled = "1"
Security::Lockdown.forums_disabled = "1"
Security::Lockdown.blips_disabled = "1"
Security::Lockdown.aiburs_disabled = "1"
Security::Lockdown.favorites_disabled = "1"
Security::Lockdown.votes_disabled = "1"
StaffAuditLog.log(:lockdown_panic, CurrentUser.user)
redirect_to security_root_path
end
def enact
params = lockdown_params
Security::Lockdown.uploads_disabled = params[:uploads] if params[:uploads].present?
Security::Lockdown.pools_disabled = params[:pools] if params[:pools].present?
Security::Lockdown.post_sets_disabled = params[:post_sets] if params[:post_sets].present?
Security::Lockdown.comments_disabled = params[:comments] if params[:comments].present?
Security::Lockdown.forums_disabled = params[:forums] if params[:forums].present?
Security::Lockdown.blips_disabled = params[:blips] if params[:blips].present?
Security::Lockdown.aiburs_disabled = params[:aiburs] if params[:aiburs].present?
Security::Lockdown.favorites_disabled = params[:favorites] if params[:favorites].present?
Security::Lockdown.votes_disabled = params[:votes] if params[:votes].present?
StaffAuditLog.log(:lockdown_uploads, CurrentUser.user, { params: params })
redirect_to security_root_path
end
def uploads_min_level
new_level = params[:uploads_min_level][:min_level].to_i
raise ArgumentError, "#{new_level} is not valid" unless User.level_hash.values.include? new_level
if new_level != Lockdown.uploads_min_level
Security::Lockdown.uploads_min_level = new_level
StaffAuditLog.log(:min_upload_level, CurrentUser.user, { level: new_level })
end
redirect_to security_root_path
end
def uploads_hide_pending
duration = params[:uploads_hide_pending][:duration].to_f
if duration >= 0 && duration != Security::Lockdown.hide_pending_posts_for
Security::Lockdown.hide_pending_posts_for = duration
StaffAuditLog.log(:hide_pending_posts_for, CurrentUser.user, { duration: duration })
end
redirect_to security_root_path
end
def lockdown_params
permitted_params = %i[uploads pools post_sets comments forums blips aiburs favorites votes]
params.fetch(:lockdown, {}).permit(permitted_params)
end
end
end

View File

@ -2,6 +2,7 @@
class TagAliasRequestsController < ApplicationController
before_action :member_only
before_action :ensure_lockdown_disabled
def new
end
@ -19,11 +20,15 @@ class TagAliasRequestsController < ApplicationController
end
end
private
private
def tar_params
permitted = %i{antecedent_name consequent_name reason}
permitted = %i[antecedent_name consequent_name reason]
permitted += [:skip_forum] if CurrentUser.is_admin?
params.require(:tag_alias_request).permit(permitted)
end
def ensure_lockdown_disabled
access_denied if Security::Lockdown.aiburs_disabled? && !CurrentUser.is_staff?
end
end

View File

@ -2,6 +2,7 @@
class TagImplicationRequestsController < ApplicationController
before_action :member_only
before_action :ensure_lockdown_disabled
def new
end
@ -19,11 +20,15 @@ class TagImplicationRequestsController < ApplicationController
end
end
private
private
def tir_params
permitted = %i{antecedent_name consequent_name reason}
permitted = %i[antecedent_name consequent_name reason]
permitted += [:skip_forum] if CurrentUser.is_admin?
params.require(:tag_implication_request).permit(permitted)
end
def ensure_lockdown_disabled
access_denied if Security::Lockdown.aiburs_disabled? && !CurrentUser.is_staff?
end
end

View File

@ -3,7 +3,7 @@
class UploadsController < ApplicationController
before_action :member_only
before_action :janitor_only, only: [:index, :show]
before_action :ensure_uploads_enabled, only: [:new, :create]
before_action :ensure_uploads_enabled, only: %i[new create]
respond_to :html, :json
content_security_policy only: [:new] do |p|
p.img_src :self, :data, :blob, "*"
@ -82,8 +82,6 @@ class UploadsController < ApplicationController
end
def ensure_uploads_enabled
if DangerZone.uploads_disabled?(CurrentUser.user)
access_denied "Uploads are disabled"
end
access_denied if Security::Lockdown.uploads_disabled? || CurrentUser.user.level < Security::Lockdown.uploads_min_level
end
end

View File

@ -53,6 +53,7 @@
@import "specific/guest_warning.scss";
@import "specific/iqdb_queries.scss";
@import "specific/keyboard_shortcuts.scss";
@import "specific/lockdown.scss";
@import "specific/maintenance.scss";
@import "specific/meta_searches.scss";
@import "specific/moderator_dashboard.scss";

View File

@ -0,0 +1,23 @@
.settings-section {
background-color: themed("color-section");
border-radius: $border-radius-half;
padding: 0.5em 0.75em;
margin-bottom: 0.5em;
.simple_form {
background-color: unset !important;
padding: 0 !important;
}
}
form.lockdown-form {
div.input {
display: flex;
gap: 0.75em;
margin-bottom: 0.25em;
input[type="checkbox"] {
order: -1;
}
}
}

View File

@ -1,35 +0,0 @@
# frozen_string_literal: true
module DangerZone
def self.uploads_disabled?(user)
user.level < min_upload_level
end
def self.post_visible?(post, user)
if hide_pending_posts_for <= 0
return true
end
post.uploader_id == user.id || user.is_staff? || !post.is_pending? || post.created_at.before?(hide_pending_posts_for.hours.ago)
end
def self.min_upload_level
(Cache.redis.get("min_upload_level") || User::Levels::MEMBER).to_i
rescue Redis::CannotConnectError
User::Levels::ADMIN + 1
end
def self.min_upload_level=(min_upload_level)
Cache.redis.set("min_upload_level", min_upload_level)
end
def self.hide_pending_posts_for
Cache.redis.get("hide_pending_posts_for").to_f || 0
rescue Redis::CannotConnectError
PostPruner::DELETION_WINDOW * 24
end
def self.hide_pending_posts_for=(duration)
Cache.redis.set("hide_pending_posts_for", duration)
end
end

View File

@ -315,12 +315,12 @@ class ElasticPostQueryBuilder < ElasticQueryBuilder
order.push({id: :desc})
end
if !CurrentUser.user.is_staff? && DangerZone.hide_pending_posts_for > 0
if !CurrentUser.user.is_staff? && Security::Lockdown.hide_pending_posts_for > 0
should = [
{
range: {
created_at: {
lte: DangerZone.hide_pending_posts_for.hours.ago,
lte: Security::Lockdown.hide_pending_posts_for.hours.ago,
},
},
},

View File

@ -0,0 +1,126 @@
# frozen_string_literal: true
module Security
module Lockdown
# Panic
def self.uploads_disabled?
Cache.redis.get("uploads_disabled") == "true"
rescue Redis::CannotConnectError
true
end
def self.uploads_disabled=(state)
Cache.redis.set("uploads_disabled", state == "1")
end
def self.pools_disabled?
Cache.redis.get("pools_disabled") == "true"
rescue Redis::CannotConnectError
true
end
def self.pools_disabled=(state)
Cache.redis.set("pools_disabled", state == "1")
end
def self.post_sets_disabled?
Cache.redis.get("post_sets_disabled") == "true"
rescue Redis::CannotConnectError
true
end
def self.post_sets_disabled=(state)
Cache.redis.set("post_sets_disabled", state == "1")
end
def self.comments_disabled?
Cache.redis.get("comments_disabled") == "true"
rescue Redis::CannotConnectError
true
end
def self.comments_disabled=(state)
Cache.redis.set("comments_disabled", state == "1")
end
def self.forums_disabled?
Cache.redis.get("forums_disabled") == "true"
rescue Redis::CannotConnectError
true
end
def self.forums_disabled=(state)
Cache.redis.set("forums_disabled", state == "1")
end
def self.blips_disabled?
Cache.redis.get("blips_disabled") == "true"
rescue Redis::CannotConnectError
true
end
def self.blips_disabled=(state)
Cache.redis.set("blips_disabled", state == "1")
end
def self.aiburs_disabled?
Cache.redis.get("aiburs_disabled") == "true"
rescue Redis::CannotConnectError
true
end
def self.aiburs_disabled=(state)
Cache.redis.set("aiburs_disabled", state == "1")
end
def self.favorites_disabled?
Cache.redis.get("favorites_disabled") == "true"
rescue Redis::CannotConnectError
true
end
def self.favorites_disabled=(state)
Cache.redis.set("favorites_disabled", state == "1")
end
def self.votes_disabled?
Cache.redis.get("votes_disabled") == "true"
rescue Redis::CannotConnectError
true
end
def self.votes_disabled=(state)
Cache.redis.set("votes_disabled", state == "1")
end
# Uploader level override
def self.uploads_min_level
(Cache.redis.get("min_upload_level") || User::Levels::MEMBER).to_i
rescue Redis::CannotConnectError
User::Levels::ADMIN + 1
end
def self.uploads_min_level=(min_upload_level)
Cache.redis.set("min_upload_level", min_upload_level)
end
# Hiding pending posts
def self.hide_pending_posts_for
Cache.redis.get("hide_pending_posts_for").to_f || 0
rescue Redis::CannotConnectError
PostPruner::DELETION_WINDOW * 24
end
def self.hide_pending_posts_for=(duration)
Cache.redis.set("hide_pending_posts_for", duration)
end
def self.post_visible?(post, user)
if hide_pending_posts_for <= 0
return true
end
post.uploader_id == user.id || user.is_staff? || !post.is_pending? || post.created_at.before?(hide_pending_posts_for.hours.ago)
end
end
end

View File

@ -1,25 +0,0 @@
<div id="c-danger-zone">
<div id="a-index">
<h1>Danger Zone</h1>
<h2>Uploads</h2>
Uploads are currently permitted for <b><%= User.level_string(DangerZone.min_upload_level) %></b> and up.
<%= custom_form_for(:uploading_limits, url: uploading_limits_admin_danger_zone_index_path, method: :put) do |f| %>
<%= f.input :min_level, collection: User.level_hash.select {|k,v| v >= User::Levels::MEMBER }.to_a, selected: DangerZone.min_upload_level %>
<%= f.button :submit, value: "Submit" %>
<% end %>
<h2>Pending Posts</h2>
<% if DangerZone.hide_pending_posts_for > 0 %>
Unapproved posts are currently only visible to staff for <b><%= DangerZone.hide_pending_posts_for %></b> hours.
<% else %>
Unapproved posts are currently not hidden.
<% end %>
<%= custom_form_for(:hide_pending_posts, url: hide_pending_posts_admin_danger_zone_index_path, method: :put) do |f| %>
<%= f.input :duration, as: :float, hint: "in hours", input_html: { value: DangerZone.hide_pending_posts_for } %>
<%= f.button :submit, value: "Submit" %>
<% end %>
</div>
</div>
<% content_for(:page_title) do %>
Danger Zone
<% end %>

View File

@ -0,0 +1,53 @@
<div id="c-security">
<div id="a-index">
<h1>Security</h1>
<section class="settings-section">
<h2>Lockdown</h2>
<%= custom_form_for(:panic, url: panic_security_lockdown_index_path, method: :put, html: { class: "lockdown-form" }) do |f| %>
<%= f.button :submit, value: "Enable Read-Only Mode" %>
<% end %>
<%= custom_form_for(:lockdown, url: enact_security_lockdown_index_path, method: :put, html: { class: "lockdown-form" }) do |f| %>
<%= f.input :uploads, as: :boolean, label: "Uploads", input_html: { checked: Security::Lockdown.uploads_disabled? ? "checked" : "" } %>
<%= f.input :pools, as: :boolean, label: "Pools", input_html: { checked: Security::Lockdown.pools_disabled? ? "checked" : "" } %>
<%= f.input :post_sets, as: :boolean, label: "Post Sets", input_html: { checked: Security::Lockdown.post_sets_disabled? ? "checked" : "" } %>
<br />
<%= f.input :comments, as: :boolean, label: "Comments", input_html: { checked: Security::Lockdown.comments_disabled? ? "checked" : "" } %>
<%= f.input :forums, as: :boolean, label: "Forums", input_html: { checked: Security::Lockdown.forums_disabled? ? "checked" : "" } %>
<%= f.input :blips, as: :boolean, label: "Blips", input_html: { checked: Security::Lockdown.blips_disabled? ? "checked" : "" } %>
<br />
<%= f.input :aiburs, as: :boolean, label: "AIBURs", input_html: { checked: Security::Lockdown.aiburs_disabled? ? "checked" : "" } %>
<%= f.input :favorites, as: :boolean, label: "Favorites", input_html: { checked: Security::Lockdown.favorites_disabled? ? "checked" : "" } %>
<%= f.input :votes, as: :boolean, label: "Votes", input_html: { checked: Security::Lockdown.votes_disabled? ? "checked" : "" } %>
<br />
<%= f.button :submit, value: "Submit" %>
<% end %>
</section>
<section class="settings-section">
<h2>Uploads</h2>
Uploads are currently permitted for <b><%= User.level_string(Security::Lockdown.uploads_min_level) %></b> and up.
<%= custom_form_for(:uploads_min_level, url: uploads_min_level_security_lockdown_index_path, method: :put) do |f| %>
<%= f.input :min_level, collection: User.level_hash.select { |_k, v| v >= User::Levels::MEMBER }.to_a, selected: Security::Lockdown.uploads_min_level %>
<%= f.button :submit, value: "Submit" %>
<% end %>
<h2>Pending Posts</h2>
<% if Security::Lockdown.hide_pending_posts_for > 0 %>
Unapproved posts are currently only visible to staff for <b><%= Security::Lockdown.hide_pending_posts_for %></b> hours.
<% else %>
Unapproved posts are currently not hidden.
<% end %>
<%= custom_form_for(:uploads_hide_pending, url: uploads_hide_pending_security_lockdown_index_path, method: :put) do |f| %>
<%= f.input :duration, as: :float, hint: "in hours", input_html: { value: Security::Lockdown.hide_pending_posts_for } %>
<%= f.button :submit, value: "Submit" %>
<% end %>
</section>
</div>
</div>
<% content_for(:page_title) do %>
Security
<% end %>

View File

@ -113,7 +113,7 @@
<li><%= link_to("Admin Dashboard", admin_dashboard_path) %></li>
<li><%= link_to("Email Blacklist", email_blacklists_path) %></li>
<li><%= link_to("Post Report Reasons", post_report_reasons_path) %></li>
<li><%= link_to("Danger Zone", admin_danger_zone_index_path) %></li>
<li><%= link_to("Security", security_root_path) %></li>
<li><%= link_to("IP Bans", ip_bans_path) %></li>
<li><%= link_to("Alt list", alt_list_admin_users_path) %></li>
<li><%= link_to("Stuck DNP tags", new_admin_stuck_dnp_path) %></li>

View File

@ -26,13 +26,21 @@ Rails.application.routes.draw do
resource :stuck_dnp, controller: "stuck_dnp", only: %i[new create]
resources :destroyed_posts, only: %i[index show update]
resources :staff_notes, only: [:index]
resources :danger_zone, only: [:index] do
end
namespace :security do
root to: "dashboard#index"
resource :dashboard, only: [:index]
resources :lockdown, only: [:index] do
collection do
put :uploading_limits
put :hide_pending_posts
put :panic
put :enact
put :uploads_min_level
put :uploads_hide_pending
end
end
end
resources :edit_histories
namespace :moderator do
resource :dashboard, :only => [:show]

View File

@ -1,37 +0,0 @@
# frozen_string_literal: true
require "test_helper"
class Admin::DangerZoneControllerTest < ActionDispatch::IntegrationTest
context "The danger zone controller" do
setup do
@admin = create(:admin_user)
end
teardown do
DangerZone.min_upload_level = User::Levels::MEMBER
DangerZone.hide_pending_posts_for = 0
end
context "index action" do
should "render" do
get_auth admin_danger_zone_index_path, @admin
assert_response :success
end
end
context "uploading limits action" do
should "work" do
put_auth uploading_limits_admin_danger_zone_index_path, @admin, params: { uploading_limits: { min_level: User::Levels::PRIVILEGED } }
assert_equal DangerZone.min_upload_level, User::Levels::PRIVILEGED
end
end
context "hide pending posts action" do
should "work" do
put_auth hide_pending_posts_admin_danger_zone_index_path, @admin, params: { hide_pending_posts: { duration: 24 } }
assert_equal DangerZone.hide_pending_posts_for, 24
end
end
end
end

View File

@ -0,0 +1,39 @@
# frozen_string_literal: true
require "test_helper"
module Security
class LockdownControllerTest < ActionDispatch::IntegrationTest
context "The lockdown controller" do
setup do
@admin = create(:admin_user)
end
teardown do
Security::Lockdown.uploads_min_level = User::Levels::MEMBER
Security::Lockdown.hide_pending_posts_for = 0
end
context "index action" do
should "render" do
get_auth security_root_path, @admin
assert_response :success
end
end
context "uploading limits action" do
should "work" do
put_auth uploads_min_level_security_lockdown_index_path, @admin, params: { uploads_min_level: { min_level: User::Levels::PRIVILEGED } }
assert_equal Security::Lockdown.uploads_min_level, User::Levels::PRIVILEGED
end
end
context "hide pending posts action" do
should "work" do
put_auth uploads_hide_pending_security_lockdown_index_path, @admin, params: { uploads_hide_pending: { duration: 24 } }
assert_equal Security::Lockdown.hide_pending_posts_for, 24
end
end
end
end
end

View File

@ -43,11 +43,11 @@ class UploadsControllerTest < ActionDispatch::IntegrationTest
context "when uploads are disabled" do
setup do
DangerZone.min_upload_level = User::Levels::PRIVILEGED
Security::Lockdown.uploads_min_level = User::Levels::PRIVILEGED
end
teardown do
DangerZone.min_upload_level = User::Levels::MEMBER
Security::Lockdown.uploads_min_level = User::Levels::MEMBER
end
should "prevent uploads" do