mirror of
https://github.com/mastodon/mastodon
synced 2024-11-27 06:18:17 +09:00
Add customizable user roles (#18641)
* Add customizable user roles * Various fixes and improvements * Add migration for old settings and fix tootctl role management
This commit is contained in:
parent
1b4054256f
commit
44b2ee3485
@ -67,7 +67,7 @@ Lint/UselessAccessModifier:
|
||||
- class_methods
|
||||
|
||||
Metrics/AbcSize:
|
||||
Max: 100
|
||||
Max: 115
|
||||
Exclude:
|
||||
- 'lib/mastodon/*_cli.rb'
|
||||
|
||||
@ -84,7 +84,7 @@ Metrics/BlockNesting:
|
||||
|
||||
Metrics/ClassLength:
|
||||
CountComments: false
|
||||
Max: 400
|
||||
Max: 500
|
||||
Exclude:
|
||||
- 'lib/mastodon/*_cli.rb'
|
||||
|
||||
|
@ -5,11 +5,15 @@ module Admin
|
||||
before_action :set_account
|
||||
|
||||
def new
|
||||
authorize @account, :show?
|
||||
|
||||
@account_action = Admin::AccountAction.new(type: params[:type], report_id: params[:report_id], send_email_notification: true, include_statuses: true)
|
||||
@warning_presets = AccountWarningPreset.all
|
||||
end
|
||||
|
||||
def create
|
||||
authorize @account, :show?
|
||||
|
||||
account_action = Admin::AccountAction.new(resource_params)
|
||||
account_action.target_account = @account
|
||||
account_action.current_account = current_account
|
||||
|
@ -14,6 +14,8 @@ module Admin
|
||||
end
|
||||
|
||||
def batch
|
||||
authorize :account, :index?
|
||||
|
||||
@form = Form::AccountBatch.new(form_account_batch_params.merge(current_account: current_account, action: action_from_button))
|
||||
@form.save
|
||||
rescue ActionController::ParameterMissing
|
||||
|
@ -4,7 +4,10 @@ module Admin
|
||||
class ActionLogsController < BaseController
|
||||
before_action :set_action_logs
|
||||
|
||||
def index; end
|
||||
def index
|
||||
authorize :audit_log, :index?
|
||||
@auditable_accounts = Account.where(id: Admin::ActionLog.reorder(nil).select('distinct account_id')).select(:id, :username)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
|
@ -7,8 +7,8 @@ module Admin
|
||||
|
||||
layout 'admin'
|
||||
|
||||
before_action :require_staff!
|
||||
before_action :set_body_classes
|
||||
after_action :verify_authorized
|
||||
|
||||
private
|
||||
|
||||
|
@ -29,6 +29,8 @@ module Admin
|
||||
end
|
||||
|
||||
def batch
|
||||
authorize :custom_emoji, :index?
|
||||
|
||||
@form = Form::CustomEmojiBatch.new(form_custom_emoji_batch_params.merge(current_account: current_account, action: action_from_button))
|
||||
@form.save
|
||||
rescue ActionController::ParameterMissing
|
||||
|
@ -5,7 +5,9 @@ module Admin
|
||||
include Redisable
|
||||
|
||||
def index
|
||||
@system_checks = Admin::SystemCheck.perform
|
||||
authorize :dashboard, :index?
|
||||
|
||||
@system_checks = Admin::SystemCheck.perform(current_user)
|
||||
@time_period = (29.days.ago.to_date...Time.now.utc.to_date)
|
||||
@pending_users_count = User.pending.count
|
||||
@pending_reports_count = Report.unresolved.count
|
||||
|
@ -12,6 +12,8 @@ module Admin
|
||||
end
|
||||
|
||||
def batch
|
||||
authorize :email_domain_block, :index?
|
||||
|
||||
@form = Form::EmailDomainBlockBatch.new(form_email_domain_block_batch_params.merge(current_account: current_account, action: action_from_button))
|
||||
@form.save
|
||||
rescue ActionController::ParameterMissing
|
||||
|
@ -12,6 +12,8 @@ module Admin
|
||||
end
|
||||
|
||||
def update
|
||||
authorize :follow_recommendation, :show?
|
||||
|
||||
@form = Form::AccountBatch.new(form_account_batch_params.merge(current_account: current_account, action: action_from_button))
|
||||
@form.save
|
||||
rescue ActionController::ParameterMissing
|
||||
|
@ -29,6 +29,8 @@ module Admin
|
||||
end
|
||||
|
||||
def batch
|
||||
authorize :ip_block, :index?
|
||||
|
||||
@form = Form::IpBlockBatch.new(form_ip_block_batch_params.merge(current_account: current_account, action: action_from_button))
|
||||
@form.save
|
||||
rescue ActionController::ParameterMissing
|
||||
|
@ -7,7 +7,7 @@ module Admin
|
||||
PER_PAGE = 40
|
||||
|
||||
def index
|
||||
authorize :account, :index?
|
||||
authorize @account, :show?
|
||||
|
||||
@accounts = RelationshipFilter.new(@account, filter_params).results.includes(:account_stat, user: [:ips, :invite_request]).page(params[:page]).per(PER_PAGE)
|
||||
@form = Form::AccountBatch.new
|
||||
|
@ -2,20 +2,63 @@
|
||||
|
||||
module Admin
|
||||
class RolesController < BaseController
|
||||
before_action :set_user
|
||||
before_action :set_role, except: [:index, :new, :create]
|
||||
|
||||
def promote
|
||||
authorize @user, :promote?
|
||||
@user.promote!
|
||||
log_action :promote, @user
|
||||
redirect_to admin_account_path(@user.account_id)
|
||||
def index
|
||||
authorize :user_role, :index?
|
||||
|
||||
@roles = UserRole.order(position: :desc).page(params[:page])
|
||||
end
|
||||
|
||||
def demote
|
||||
authorize @user, :demote?
|
||||
@user.demote!
|
||||
log_action :demote, @user
|
||||
redirect_to admin_account_path(@user.account_id)
|
||||
def new
|
||||
authorize :user_role, :create?
|
||||
|
||||
@role = UserRole.new
|
||||
end
|
||||
|
||||
def create
|
||||
authorize :user_role, :create?
|
||||
|
||||
@role = UserRole.new(resource_params)
|
||||
@role.current_account = current_account
|
||||
|
||||
if @role.save
|
||||
redirect_to admin_roles_path
|
||||
else
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
authorize @role, :update?
|
||||
end
|
||||
|
||||
def update
|
||||
authorize @role, :update?
|
||||
|
||||
@role.current_account = current_account
|
||||
|
||||
if @role.update(resource_params)
|
||||
redirect_to admin_roles_path
|
||||
else
|
||||
render :edit
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize @role, :destroy?
|
||||
@role.destroy!
|
||||
redirect_to admin_roles_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_role
|
||||
@role = UserRole.find(params[:id])
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params.require(:user_role).permit(:name, :color, :highlighted, :position, permissions_as_keys: [])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -14,6 +14,8 @@ module Admin
|
||||
end
|
||||
|
||||
def batch
|
||||
authorize :status, :index?
|
||||
|
||||
@status_batch_action = Admin::StatusBatchAction.new(admin_status_batch_action_params.merge(current_account: current_account, report_id: params[:report_id], type: action_from_button))
|
||||
@status_batch_action.save!
|
||||
rescue ActionController::ParameterMissing
|
||||
|
@ -1,20 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class SubscriptionsController < BaseController
|
||||
def index
|
||||
authorize :subscription, :index?
|
||||
@subscriptions = ordered_subscriptions.page(requested_page)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ordered_subscriptions
|
||||
Subscription.order(id: :desc).includes(:account)
|
||||
end
|
||||
|
||||
def requested_page
|
||||
params[:page].to_i
|
||||
end
|
||||
end
|
||||
end
|
@ -2,13 +2,15 @@
|
||||
|
||||
class Admin::Trends::Links::PreviewCardProvidersController < Admin::BaseController
|
||||
def index
|
||||
authorize :preview_card_provider, :index?
|
||||
authorize :preview_card_provider, :review?
|
||||
|
||||
@preview_card_providers = filtered_preview_card_providers.page(params[:page])
|
||||
@form = Trends::PreviewCardProviderBatch.new
|
||||
end
|
||||
|
||||
def batch
|
||||
authorize :preview_card_provider, :review?
|
||||
|
||||
@form = Trends::PreviewCardProviderBatch.new(trends_preview_card_provider_batch_params.merge(current_account: current_account, action: action_from_button))
|
||||
@form.save
|
||||
rescue ActionController::ParameterMissing
|
||||
|
@ -2,13 +2,15 @@
|
||||
|
||||
class Admin::Trends::LinksController < Admin::BaseController
|
||||
def index
|
||||
authorize :preview_card, :index?
|
||||
authorize :preview_card, :review?
|
||||
|
||||
@preview_cards = filtered_preview_cards.page(params[:page])
|
||||
@form = Trends::PreviewCardBatch.new
|
||||
end
|
||||
|
||||
def batch
|
||||
authorize :preview_card, :review?
|
||||
|
||||
@form = Trends::PreviewCardBatch.new(trends_preview_card_batch_params.merge(current_account: current_account, action: action_from_button))
|
||||
@form.save
|
||||
rescue ActionController::ParameterMissing
|
||||
|
@ -2,13 +2,15 @@
|
||||
|
||||
class Admin::Trends::StatusesController < Admin::BaseController
|
||||
def index
|
||||
authorize :status, :index?
|
||||
authorize :status, :review?
|
||||
|
||||
@statuses = filtered_statuses.page(params[:page])
|
||||
@form = Trends::StatusBatch.new
|
||||
end
|
||||
|
||||
def batch
|
||||
authorize :status, :review?
|
||||
|
||||
@form = Trends::StatusBatch.new(trends_status_batch_params.merge(current_account: current_account, action: action_from_button))
|
||||
@form.save
|
||||
rescue ActionController::ParameterMissing
|
||||
|
@ -2,13 +2,15 @@
|
||||
|
||||
class Admin::Trends::TagsController < Admin::BaseController
|
||||
def index
|
||||
authorize :tag, :index?
|
||||
authorize :tag, :review?
|
||||
|
||||
@tags = filtered_tags.page(params[:page])
|
||||
@form = Trends::TagBatch.new
|
||||
end
|
||||
|
||||
def batch
|
||||
authorize :tag, :review?
|
||||
|
||||
@form = Trends::TagBatch.new(trends_tag_batch_params.merge(current_account: current_account, action: action_from_button))
|
||||
@form.save
|
||||
rescue ActionController::ParameterMissing
|
||||
|
33
app/controllers/admin/users/roles_controller.rb
Normal file
33
app/controllers/admin/users/roles_controller.rb
Normal file
@ -0,0 +1,33 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class Users::RolesController < BaseController
|
||||
before_action :set_user
|
||||
|
||||
def show
|
||||
authorize @user, :change_role?
|
||||
end
|
||||
|
||||
def update
|
||||
authorize @user, :change_role?
|
||||
|
||||
@user.current_account = current_account
|
||||
|
||||
if @user.update(resource_params)
|
||||
redirect_to admin_account_path(@user.account_id), notice: I18n.t('admin.accounts.change_role.changed_msg')
|
||||
else
|
||||
render :show
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_user
|
||||
@user = User.find(params[:user_id])
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params.require(:user).permit(:role_id)
|
||||
end
|
||||
end
|
||||
end
|
@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class TwoFactorAuthenticationsController < BaseController
|
||||
class Users::TwoFactorAuthenticationsController < BaseController
|
||||
before_action :set_target_user
|
||||
|
||||
def destroy
|
@ -1,11 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Admin::AccountActionsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { authorize_if_got_token! :'admin:write', :'admin:write:accounts' }
|
||||
before_action :require_staff!
|
||||
before_action :set_account
|
||||
|
||||
after_action :verify_authorized
|
||||
|
||||
def create
|
||||
authorize @account, :show?
|
||||
|
||||
account_action = Admin::AccountAction.new(resource_params)
|
||||
account_action.target_account = @account
|
||||
account_action.current_account = current_account
|
||||
|
@ -8,11 +8,11 @@ class Api::V1::Admin::AccountsController < Api::BaseController
|
||||
|
||||
before_action -> { authorize_if_got_token! :'admin:read', :'admin:read:accounts' }, only: [:index, :show]
|
||||
before_action -> { authorize_if_got_token! :'admin:write', :'admin:write:accounts' }, except: [:index, :show]
|
||||
before_action :require_staff!
|
||||
before_action :set_accounts, only: :index
|
||||
before_action :set_account, except: :index
|
||||
before_action :require_local_account!, only: [:enable, :approve, :reject]
|
||||
|
||||
after_action :verify_authorized
|
||||
after_action :insert_pagination_headers, only: :index
|
||||
|
||||
FILTER_PARAMS = %i(
|
||||
@ -119,7 +119,9 @@ class Api::V1::Admin::AccountsController < Api::BaseController
|
||||
translated_params[:status] = status.to_s if params[status].present?
|
||||
end
|
||||
|
||||
translated_params[:permissions] = 'staff' if params[:staff].present?
|
||||
if params[:staff].present?
|
||||
translated_params[:role_ids] = UserRole.that_can(:manage_reports).map(&:id)
|
||||
end
|
||||
|
||||
translated_params
|
||||
end
|
||||
|
@ -1,11 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Admin::DimensionsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { authorize_if_got_token! :'admin:read' }
|
||||
before_action :require_staff!
|
||||
before_action :set_dimensions
|
||||
|
||||
after_action :verify_authorized
|
||||
|
||||
def create
|
||||
authorize :dashboard, :index?
|
||||
render json: @dimensions, each_serializer: REST::Admin::DimensionSerializer
|
||||
end
|
||||
|
||||
|
@ -8,10 +8,10 @@ class Api::V1::Admin::DomainAllowsController < Api::BaseController
|
||||
|
||||
before_action -> { authorize_if_got_token! :'admin:read', :'admin:read:domain_allows' }, only: [:index, :show]
|
||||
before_action -> { authorize_if_got_token! :'admin:write', :'admin:write:domain_allows' }, except: [:index, :show]
|
||||
before_action :require_staff!
|
||||
before_action :set_domain_allows, only: :index
|
||||
before_action :set_domain_allow, only: [:show, :destroy]
|
||||
|
||||
after_action :verify_authorized
|
||||
after_action :insert_pagination_headers, only: :index
|
||||
|
||||
PAGINATION_PARAMS = %i(limit).freeze
|
||||
|
@ -8,10 +8,10 @@ class Api::V1::Admin::DomainBlocksController < Api::BaseController
|
||||
|
||||
before_action -> { authorize_if_got_token! :'admin:read', :'admin:read:domain_blocks' }, only: [:index, :show]
|
||||
before_action -> { authorize_if_got_token! :'admin:write', :'admin:write:domain_blocks' }, except: [:index, :show]
|
||||
before_action :require_staff!
|
||||
before_action :set_domain_blocks, only: :index
|
||||
before_action :set_domain_block, only: [:show, :update, :destroy]
|
||||
|
||||
after_action :verify_authorized
|
||||
after_action :insert_pagination_headers, only: :index
|
||||
|
||||
PAGINATION_PARAMS = %i(limit).freeze
|
||||
|
@ -1,11 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Admin::MeasuresController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { authorize_if_got_token! :'admin:read' }
|
||||
before_action :require_staff!
|
||||
before_action :set_measures
|
||||
|
||||
after_action :verify_authorized
|
||||
|
||||
def create
|
||||
authorize :dashboard, :index?
|
||||
render json: @measures, each_serializer: REST::Admin::MeasureSerializer
|
||||
end
|
||||
|
||||
|
@ -8,10 +8,10 @@ class Api::V1::Admin::ReportsController < Api::BaseController
|
||||
|
||||
before_action -> { authorize_if_got_token! :'admin:read', :'admin:read:reports' }, only: [:index, :show]
|
||||
before_action -> { authorize_if_got_token! :'admin:write', :'admin:write:reports' }, except: [:index, :show]
|
||||
before_action :require_staff!
|
||||
before_action :set_reports, only: :index
|
||||
before_action :set_report, except: :index
|
||||
|
||||
after_action :verify_authorized
|
||||
after_action :insert_pagination_headers, only: :index
|
||||
|
||||
FILTER_PARAMS = %i(
|
||||
|
@ -1,11 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Admin::RetentionController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { authorize_if_got_token! :'admin:read' }
|
||||
before_action :require_staff!
|
||||
before_action :set_cohorts
|
||||
|
||||
after_action :verify_authorized
|
||||
|
||||
def create
|
||||
authorize :dashboard, :index?
|
||||
render json: @cohorts, each_serializer: REST::Admin::CohortSerializer
|
||||
end
|
||||
|
||||
|
@ -1,17 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Admin::Trends::LinksController < Api::BaseController
|
||||
class Api::V1::Admin::Trends::LinksController < Api::V1::Trends::LinksController
|
||||
before_action -> { authorize_if_got_token! :'admin:read' }
|
||||
before_action :require_staff!
|
||||
before_action :set_links
|
||||
|
||||
def index
|
||||
render json: @links, each_serializer: REST::Trends::LinkSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_links
|
||||
@links = Trends.links.query.limit(limit_param(10))
|
||||
def enabled?
|
||||
super || current_user&.can?(:manage_taxonomies)
|
||||
end
|
||||
|
||||
def links_from_trends
|
||||
if current_user&.can?(:manage_taxonomies)
|
||||
Trends.links.query
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,17 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Admin::Trends::StatusesController < Api::BaseController
|
||||
class Api::V1::Admin::Trends::StatusesController < Api::V1::Trends::StatusesController
|
||||
before_action -> { authorize_if_got_token! :'admin:read' }
|
||||
before_action :require_staff!
|
||||
before_action :set_statuses
|
||||
|
||||
def index
|
||||
render json: @statuses, each_serializer: REST::StatusSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_statuses
|
||||
@statuses = cache_collection(Trends.statuses.query.limit(limit_param(DEFAULT_STATUSES_LIMIT)), Status)
|
||||
def enabled?
|
||||
super || current_user&.can?(:manage_taxonomies)
|
||||
end
|
||||
|
||||
def statuses_from_trends
|
||||
if current_user&.can?(:manage_taxonomies)
|
||||
Trends.statuses.query
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,17 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Admin::Trends::TagsController < Api::BaseController
|
||||
class Api::V1::Admin::Trends::TagsController < Api::V1::Trends::TagsController
|
||||
before_action -> { authorize_if_got_token! :'admin:read' }
|
||||
before_action :require_staff!
|
||||
before_action :set_tags
|
||||
|
||||
def index
|
||||
render json: @tags, each_serializer: REST::Admin::TagSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_tags
|
||||
@tags = Trends.tags.query.limit(limit_param(10))
|
||||
def enabled?
|
||||
super || current_user&.can?(:manage_taxonomies)
|
||||
end
|
||||
|
||||
def tags_from_trends
|
||||
if current_user&.can?(:manage_taxonomies)
|
||||
Trends.tags.query
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -13,10 +13,14 @@ class Api::V1::Trends::LinksController < Api::BaseController
|
||||
|
||||
private
|
||||
|
||||
def enabled?
|
||||
Setting.trends
|
||||
end
|
||||
|
||||
def set_links
|
||||
@links = begin
|
||||
if Setting.trends
|
||||
links_from_trends
|
||||
if enabled?
|
||||
links_from_trends.offset(offset_param).limit(limit_param(DEFAULT_LINKS_LIMIT))
|
||||
else
|
||||
[]
|
||||
end
|
||||
@ -24,7 +28,7 @@ class Api::V1::Trends::LinksController < Api::BaseController
|
||||
end
|
||||
|
||||
def links_from_trends
|
||||
Trends.links.query.allowed.in_locale(content_locale).offset(offset_param).limit(limit_param(DEFAULT_LINKS_LIMIT))
|
||||
Trends.links.query.allowed.in_locale(content_locale)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
|
@ -11,10 +11,14 @@ class Api::V1::Trends::StatusesController < Api::BaseController
|
||||
|
||||
private
|
||||
|
||||
def enabled?
|
||||
Setting.trends
|
||||
end
|
||||
|
||||
def set_statuses
|
||||
@statuses = begin
|
||||
if Setting.trends
|
||||
cache_collection(statuses_from_trends, Status)
|
||||
if enabled?
|
||||
cache_collection(statuses_from_trends.offset(offset_param).limit(limit_param(DEFAULT_STATUSES_LIMIT)), Status)
|
||||
else
|
||||
[]
|
||||
end
|
||||
@ -24,7 +28,7 @@ class Api::V1::Trends::StatusesController < Api::BaseController
|
||||
def statuses_from_trends
|
||||
scope = Trends.statuses.query.allowed.in_locale(content_locale)
|
||||
scope = scope.filtered_for(current_account) if user_signed_in?
|
||||
scope.offset(offset_param).limit(limit_param(DEFAULT_STATUSES_LIMIT))
|
||||
scope
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
|
@ -13,16 +13,24 @@ class Api::V1::Trends::TagsController < Api::BaseController
|
||||
|
||||
private
|
||||
|
||||
def enabled?
|
||||
Setting.trends
|
||||
end
|
||||
|
||||
def set_tags
|
||||
@tags = begin
|
||||
if Setting.trends
|
||||
Trends.tags.query.allowed.offset(offset_param).limit(limit_param(DEFAULT_TAGS_LIMIT))
|
||||
if enabled?
|
||||
tags_from_trends.offset(offset_param).limit(limit_param(DEFAULT_TAGS_LIMIT))
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def tags_from_trends
|
||||
Trends.tags.query.allowed
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
@ -11,6 +11,7 @@ class Api::V2::Admin::AccountsController < Api::V1::Admin::AccountsController
|
||||
email
|
||||
ip
|
||||
invited_by
|
||||
role_ids
|
||||
).freeze
|
||||
|
||||
PAGINATION_PARAMS = (%i(limit) + FILTER_PARAMS).freeze
|
||||
@ -18,7 +19,17 @@ class Api::V2::Admin::AccountsController < Api::V1::Admin::AccountsController
|
||||
private
|
||||
|
||||
def filtered_accounts
|
||||
AccountFilter.new(filter_params).results
|
||||
AccountFilter.new(translated_filter_params).results
|
||||
end
|
||||
|
||||
def translated_filter_params
|
||||
translated_params = filter_params.slice(*AccountFilter::KEYS)
|
||||
|
||||
if params[:permissions] == 'staff'
|
||||
translated_params[:role_ids] = UserRole.that_can(:manage_reports).map(&:id)
|
||||
end
|
||||
|
||||
translated_params
|
||||
end
|
||||
|
||||
def filter_params
|
||||
|
@ -56,14 +56,6 @@ class ApplicationController < ActionController::Base
|
||||
store_location_for(:user, request.url) unless [:json, :rss].include?(request.format&.to_sym)
|
||||
end
|
||||
|
||||
def require_admin!
|
||||
forbidden unless current_user&.admin?
|
||||
end
|
||||
|
||||
def require_staff!
|
||||
forbidden unless current_user&.staff?
|
||||
end
|
||||
|
||||
def require_functional!
|
||||
redirect_to edit_user_registration_path unless current_user.functional?
|
||||
end
|
||||
|
@ -13,6 +13,6 @@ class CustomCssController < ApplicationController
|
||||
def show
|
||||
expires_in 3.minutes, public: true
|
||||
request.session_options[:skip] = true
|
||||
render plain: Setting.custom_css || '', content_type: 'text/css'
|
||||
render content_type: 'text/css'
|
||||
end
|
||||
end
|
||||
|
@ -61,21 +61,13 @@ module AccountsHelper
|
||||
end
|
||||
end
|
||||
|
||||
def account_badge(account, all: false)
|
||||
def account_badge(account)
|
||||
if account.bot?
|
||||
content_tag(:div, content_tag(:div, t('accounts.roles.bot'), class: 'account-role bot'), class: 'roles')
|
||||
elsif account.group?
|
||||
content_tag(:div, content_tag(:div, t('accounts.roles.group'), class: 'account-role group'), class: 'roles')
|
||||
elsif (Setting.show_staff_badge && account.user_staff?) || all
|
||||
content_tag(:div, class: 'roles') do
|
||||
if all && !account.user_staff?
|
||||
content_tag(:div, t('admin.accounts.roles.user'), class: 'account-role')
|
||||
elsif account.user_admin?
|
||||
content_tag(:div, t('accounts.roles.admin'), class: 'account-role admin')
|
||||
elsif account.user_moderator?
|
||||
content_tag(:div, t('accounts.roles.moderator'), class: 'account-role moderator')
|
||||
end
|
||||
end
|
||||
elsif account.user_role&.highlighted?
|
||||
content_tag(:div, content_tag(:div, account.user_role.name, class: "account-role user-role-#{account.user_role.id}"), class: 'roles')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -6,8 +6,9 @@ import IconButton from './icon_button';
|
||||
import DropdownMenuContainer from '../containers/dropdown_menu_container';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { me, isStaff } from '../initial_state';
|
||||
import { me } from '../initial_state';
|
||||
import classNames from 'classnames';
|
||||
import { PERMISSION_MANAGE_USERS } from 'mastodon/permissions';
|
||||
|
||||
const messages = defineMessages({
|
||||
delete: { id: 'status.delete', defaultMessage: 'Delete' },
|
||||
@ -55,6 +56,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
@ -306,7 +308,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
if (isStaff) {
|
||||
if ((this.context.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) {
|
||||
menu.push(null);
|
||||
menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` });
|
||||
menu.push({ text: intl.formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses?id=${status.get('id')}` });
|
||||
|
@ -26,6 +26,7 @@ const createIdentityContext = state => ({
|
||||
signedIn: !!state.meta.me,
|
||||
accountId: state.meta.me,
|
||||
accessToken: state.meta.access_token,
|
||||
permissions: state.role.permissions,
|
||||
});
|
||||
|
||||
export default class Mastodon extends React.PureComponent {
|
||||
|
@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import Button from 'mastodon/components/button';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { autoPlayGif, me, isStaff } from 'mastodon/initial_state';
|
||||
import { autoPlayGif, me } from 'mastodon/initial_state';
|
||||
import classNames from 'classnames';
|
||||
import Icon from 'mastodon/components/icon';
|
||||
import IconButton from 'mastodon/components/icon_button';
|
||||
@ -14,6 +14,7 @@ import ShortNumber from 'mastodon/components/short_number';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container';
|
||||
import AccountNoteContainer from '../containers/account_note_container';
|
||||
import { PERMISSION_MANAGE_USERS } from 'mastodon/permissions';
|
||||
|
||||
const messages = defineMessages({
|
||||
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
|
||||
@ -64,6 +65,10 @@ const dateFormatOptions = {
|
||||
export default @injectIntl
|
||||
class Header extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
account: ImmutablePropTypes.map,
|
||||
identity_props: ImmutablePropTypes.list,
|
||||
@ -241,7 +246,7 @@ class Header extends ImmutablePureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
if (account.get('id') !== me && isStaff) {
|
||||
if (account.get('id') !== me && (this.context.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) {
|
||||
menu.push(null);
|
||||
menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: `/admin/accounts/${account.get('id')}` });
|
||||
}
|
||||
|
@ -5,10 +5,14 @@ import { FormattedMessage } from 'react-intl';
|
||||
import ClearColumnButton from './clear_column_button';
|
||||
import GrantPermissionButton from './grant_permission_button';
|
||||
import SettingToggle from './setting_toggle';
|
||||
import { isStaff } from 'mastodon/initial_state';
|
||||
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_REPORTS } from 'mastodon/permissions';
|
||||
|
||||
export default class ColumnSettings extends React.PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
settings: ImmutablePropTypes.map.isRequired,
|
||||
pushSettings: ImmutablePropTypes.map.isRequired,
|
||||
@ -166,7 +170,7 @@ export default class ColumnSettings extends React.PureComponent {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isStaff && (
|
||||
{(this.context.identity.permissions & PERMISSION_MANAGE_USERS === PERMISSION_MANAGE_USERS) && (
|
||||
<div role='group' aria-labelledby='notifications-admin-sign-up'>
|
||||
<span id='notifications-status' className='column-settings__section'><FormattedMessage id='notifications.column_settings.admin.sign_up' defaultMessage='New sign-ups:' /></span>
|
||||
|
||||
@ -179,7 +183,7 @@ export default class ColumnSettings extends React.PureComponent {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isStaff && (
|
||||
{(this.context.identity.permissions & PERMISSION_MANAGE_REPORTS === PERMISSION_MANAGE_REPORTS) && (
|
||||
<div role='group' aria-labelledby='notifications-admin-report'>
|
||||
<span id='notifications-status' className='column-settings__section'><FormattedMessage id='notifications.column_settings.admin.report' defaultMessage='New reports:' /></span>
|
||||
|
||||
|
@ -5,8 +5,9 @@ import IconButton from '../../../components/icon_button';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import DropdownMenuContainer from '../../../containers/dropdown_menu_container';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import { me, isStaff } from '../../../initial_state';
|
||||
import { me } from '../../../initial_state';
|
||||
import classNames from 'classnames';
|
||||
import { PERMISSION_MANAGE_USERS } from 'mastodon/permissions';
|
||||
|
||||
const messages = defineMessages({
|
||||
delete: { id: 'status.delete', defaultMessage: 'Delete' },
|
||||
@ -50,6 +51,7 @@ class ActionBar extends React.PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
@ -248,7 +250,7 @@ class ActionBar extends React.PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
if (isStaff) {
|
||||
if ((this.context.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) {
|
||||
menu.push(null);
|
||||
menu.push({ text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` });
|
||||
menu.push({ text: intl.formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses?id=${status.get('id')}` });
|
||||
|
@ -3,9 +3,10 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { invitesEnabled, limitedFederationMode, version, repository, source_url, profile_directory as profileDirectory } from 'mastodon/initial_state';
|
||||
import { limitedFederationMode, version, repository, source_url, profile_directory as profileDirectory } from 'mastodon/initial_state';
|
||||
import { logOut } from 'mastodon/utils/log_out';
|
||||
import { openModal } from 'mastodon/actions/modal';
|
||||
import { PERMISSION_INVITE_USERS } from 'mastodon/permissions';
|
||||
|
||||
const messages = defineMessages({
|
||||
logoutMessage: { id: 'confirmations.logout.message', defaultMessage: 'Are you sure you want to log out?' },
|
||||
@ -27,6 +28,10 @@ export default @injectIntl
|
||||
@connect(null, mapDispatchToProps)
|
||||
class LinkFooter extends React.PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
identity: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
withHotkeys: PropTypes.bool,
|
||||
onLogout: PropTypes.func.isRequired,
|
||||
@ -48,7 +53,7 @@ class LinkFooter extends React.PureComponent {
|
||||
return (
|
||||
<div className='getting-started__footer'>
|
||||
<ul>
|
||||
{invitesEnabled && <li><a href='/invites' target='_blank'><FormattedMessage id='getting_started.invite' defaultMessage='Invite people' /></a> · </li>}
|
||||
{((this.context.identity.permissions & PERMISSION_INVITE_USERS) === PERMISSION_INVITE_USERS) && <li><a href='/invites' target='_blank'><FormattedMessage id='getting_started.invite' defaultMessage='Invite people' /></a> · </li>}
|
||||
{withHotkeys && <li><Link to='/keyboard-shortcuts'><FormattedMessage id='navigation_bar.keyboard_shortcuts' defaultMessage='Hotkeys' /></Link> · </li>}
|
||||
<li><a href='/auth/edit'><FormattedMessage id='getting_started.security' defaultMessage='Security' /></a> · </li>
|
||||
{!limitedFederationMode && <li><a href='/about/more' target='_blank'><FormattedMessage id='navigation_bar.info' defaultMessage='About this server' /></a> · </li>}
|
||||
|
@ -12,14 +12,12 @@ export const boostModal = getMeta('boost_modal');
|
||||
export const deleteModal = getMeta('delete_modal');
|
||||
export const me = getMeta('me');
|
||||
export const searchEnabled = getMeta('search_enabled');
|
||||
export const invitesEnabled = getMeta('invites_enabled');
|
||||
export const limitedFederationMode = getMeta('limited_federation_mode');
|
||||
export const repository = getMeta('repository');
|
||||
export const source_url = getMeta('source_url');
|
||||
export const version = getMeta('version');
|
||||
export const mascot = getMeta('mascot');
|
||||
export const profile_directory = getMeta('profile_directory');
|
||||
export const isStaff = getMeta('is_staff');
|
||||
export const forceSingleColumn = !getMeta('advanced_layout');
|
||||
export const useBlurhash = getMeta('use_blurhash');
|
||||
export const usePendingItems = getMeta('use_pending_items');
|
||||
|
3
app/javascript/mastodon/permissions.js
Normal file
3
app/javascript/mastodon/permissions.js
Normal file
@ -0,0 +1,3 @@
|
||||
export const PERMISSION_INVITE_USERS = 0x0000000000010000;
|
||||
export const PERMISSION_MANAGE_USERS = 0x0000000000000400;
|
||||
export const PERMISSION_MANAGE_REPORTS = 0x0000000000000010;
|
@ -7,12 +7,13 @@ const initialState = ImmutableMap({
|
||||
streaming_api_base_url: null,
|
||||
access_token: null,
|
||||
layout: layoutFromWindow(),
|
||||
permissions: '0',
|
||||
});
|
||||
|
||||
export default function meta(state = initialState, action) {
|
||||
switch(action.type) {
|
||||
case STORE_HYDRATE:
|
||||
return state.merge(action.state.get('meta'));
|
||||
return state.merge(action.state.get('meta')).set('permissions', action.state.getIn(['role', 'permissions']));
|
||||
case APP_LAYOUT_CHANGE:
|
||||
return state.set('layout', action.layout);
|
||||
default:
|
||||
|
@ -924,6 +924,10 @@ a.name-tag,
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.user-role {
|
||||
color: var(--user-role-accent);
|
||||
}
|
||||
|
||||
.announcements-list,
|
||||
.filters-list {
|
||||
border: 1px solid lighten($ui-base-color, 4%);
|
||||
@ -960,6 +964,17 @@ a.name-tag,
|
||||
&__meta {
|
||||
padding: 0 15px;
|
||||
color: $dark-text-color;
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__action-bar {
|
||||
|
@ -256,6 +256,10 @@ code {
|
||||
}
|
||||
}
|
||||
|
||||
.input.with_block_label.user_role_permissions_as_keys ul {
|
||||
columns: unset;
|
||||
}
|
||||
|
||||
.input.datetime .label_input select {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
|
@ -8,11 +8,11 @@ class Admin::SystemCheck
|
||||
Admin::SystemCheck::ElasticsearchCheck,
|
||||
].freeze
|
||||
|
||||
def self.perform
|
||||
def self.perform(current_user)
|
||||
ACTIVE_CHECKS.each_with_object([]) do |klass, arr|
|
||||
check = klass.new
|
||||
check = klass.new(current_user)
|
||||
|
||||
if check.pass?
|
||||
if check.skip? || check.pass?
|
||||
arr
|
||||
else
|
||||
arr << check.message
|
||||
|
@ -1,6 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Admin::SystemCheck::BaseCheck
|
||||
attr_reader :current_user
|
||||
|
||||
def initialize(current_user)
|
||||
@current_user = current_user
|
||||
end
|
||||
|
||||
def skip?
|
||||
false
|
||||
end
|
||||
|
||||
def pass?
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
@ -1,6 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Admin::SystemCheck::DatabaseSchemaCheck < Admin::SystemCheck::BaseCheck
|
||||
def skip?
|
||||
!current_user.can?(:view_devops)
|
||||
end
|
||||
|
||||
def pass?
|
||||
!ActiveRecord::Base.connection.migration_context.needs_migration?
|
||||
end
|
||||
|
@ -1,6 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Admin::SystemCheck::ElasticsearchCheck < Admin::SystemCheck::BaseCheck
|
||||
def skip?
|
||||
!current_user.can?(:view_devops)
|
||||
end
|
||||
|
||||
def pass?
|
||||
return true unless Chewy.enabled?
|
||||
|
||||
@ -32,8 +36,4 @@ class Admin::SystemCheck::ElasticsearchCheck < Admin::SystemCheck::BaseCheck
|
||||
def compatible_version?
|
||||
Gem::Version.new(running_version) >= Gem::Version.new(required_version)
|
||||
end
|
||||
|
||||
def missing_queues
|
||||
@missing_queues ||= Sidekiq::ProcessSet.new.reduce(SIDEKIQ_QUEUES) { |queues, process| queues - process['queues'] }
|
||||
end
|
||||
end
|
||||
|
@ -3,6 +3,10 @@
|
||||
class Admin::SystemCheck::RulesCheck < Admin::SystemCheck::BaseCheck
|
||||
include RoutingHelper
|
||||
|
||||
def skip?
|
||||
!current_user.can?(:manage_rules)
|
||||
end
|
||||
|
||||
def pass?
|
||||
Rule.kept.exists?
|
||||
end
|
||||
|
@ -9,6 +9,10 @@ class Admin::SystemCheck::SidekiqProcessCheck < Admin::SystemCheck::BaseCheck
|
||||
scheduler
|
||||
).freeze
|
||||
|
||||
def skip?
|
||||
!current_user.can?(:view_devops)
|
||||
end
|
||||
|
||||
def pass?
|
||||
missing_queues.empty?
|
||||
end
|
||||
|
@ -116,7 +116,7 @@ class Account < ApplicationRecord
|
||||
scope :by_recent_status, -> { order(Arel.sql('(case when account_stats.last_status_at is null then 1 else 0 end) asc, account_stats.last_status_at desc, accounts.id desc')) }
|
||||
scope :by_recent_sign_in, -> { order(Arel.sql('(case when users.current_sign_in_at is null then 1 else 0 end) asc, users.current_sign_in_at desc, accounts.id desc')) }
|
||||
scope :popular, -> { order('account_stats.followers_count desc') }
|
||||
scope :by_domain_and_subdomains, ->(domain) { where(domain: domain).or(where(arel_table[:domain].matches('%.' + domain))) }
|
||||
scope :by_domain_and_subdomains, ->(domain) { where(domain: domain).or(where(arel_table[:domain].matches("%.#{domain}"))) }
|
||||
scope :not_excluded_by_account, ->(account) { where.not(id: account.excluded_from_timeline_account_ids) }
|
||||
scope :not_domain_blocked_by_account, ->(account) { where(arel_table[:domain].eq(nil).or(arel_table[:domain].not_in(account.excluded_from_timeline_domains))) }
|
||||
|
||||
@ -132,9 +132,6 @@ class Account < ApplicationRecord
|
||||
:unconfirmed?,
|
||||
:unconfirmed_or_pending?,
|
||||
:role,
|
||||
:admin?,
|
||||
:moderator?,
|
||||
:staff?,
|
||||
:locale,
|
||||
:shows_application?,
|
||||
to: :user,
|
||||
@ -454,7 +451,7 @@ class Account < ApplicationRecord
|
||||
DeliveryFailureTracker.without_unavailable(urls)
|
||||
end
|
||||
|
||||
def search_for(terms, limit = 10, offset = 0)
|
||||
def search_for(terms, limit: 10, offset: 0)
|
||||
tsquery = generate_query_for_search(terms)
|
||||
|
||||
sql = <<-SQL.squish
|
||||
@ -476,7 +473,7 @@ class Account < ApplicationRecord
|
||||
records
|
||||
end
|
||||
|
||||
def advanced_search_for(terms, account, limit = 10, following = false, offset = 0)
|
||||
def advanced_search_for(terms, account, limit: 10, following: false, offset: 0)
|
||||
tsquery = generate_query_for_search(terms)
|
||||
sql = advanced_search_for_sql_template(following)
|
||||
records = find_by_sql([sql, id: account.id, limit: limit, offset: offset, tsquery: tsquery])
|
||||
|
@ -4,7 +4,7 @@ class AccountFilter
|
||||
KEYS = %i(
|
||||
origin
|
||||
status
|
||||
permissions
|
||||
role_ids
|
||||
username
|
||||
by_domain
|
||||
display_name
|
||||
@ -26,7 +26,7 @@ class AccountFilter
|
||||
params.each do |key, value|
|
||||
next if key.to_s == 'page'
|
||||
|
||||
scope.merge!(scope_for(key, value.to_s.strip)) if value.present?
|
||||
scope.merge!(scope_for(key, value)) if value.present?
|
||||
end
|
||||
|
||||
scope
|
||||
@ -38,18 +38,18 @@ class AccountFilter
|
||||
case key.to_s
|
||||
when 'origin'
|
||||
origin_scope(value)
|
||||
when 'permissions'
|
||||
permissions_scope(value)
|
||||
when 'role_ids'
|
||||
role_scope(value)
|
||||
when 'status'
|
||||
status_scope(value)
|
||||
when 'by_domain'
|
||||
Account.where(domain: value)
|
||||
Account.where(domain: value.to_s)
|
||||
when 'username'
|
||||
Account.matches_username(value)
|
||||
Account.matches_username(value.to_s)
|
||||
when 'display_name'
|
||||
Account.matches_display_name(value)
|
||||
Account.matches_display_name(value.to_s)
|
||||
when 'email'
|
||||
accounts_with_users.merge(User.matches_email(value))
|
||||
accounts_with_users.merge(User.matches_email(value.to_s))
|
||||
when 'ip'
|
||||
valid_ip?(value) ? accounts_with_users.merge(User.matches_ip(value).group('users.id, accounts.id')) : Account.none
|
||||
when 'invited_by'
|
||||
@ -104,13 +104,8 @@ class AccountFilter
|
||||
Account.left_joins(user: :invite).merge(Invite.where(user_id: value.to_s))
|
||||
end
|
||||
|
||||
def permissions_scope(value)
|
||||
case value.to_s
|
||||
when 'staff'
|
||||
accounts_with_users.merge(User.staff)
|
||||
else
|
||||
raise "Unknown permissions: #{value}"
|
||||
end
|
||||
def role_scope(value)
|
||||
accounts_with_users.merge(User.where(role_id: Array(value).map(&:to_s)))
|
||||
end
|
||||
|
||||
def accounts_with_users
|
||||
@ -118,7 +113,7 @@ class AccountFilter
|
||||
end
|
||||
|
||||
def valid_ip?(value)
|
||||
IPAddr.new(value) && true
|
||||
IPAddr.new(value.to_s) && true
|
||||
rescue IPAddr::InvalidAddressError
|
||||
false
|
||||
end
|
||||
|
@ -1,68 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module UserRoles
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
scope :admins, -> { where(admin: true) }
|
||||
scope :moderators, -> { where(moderator: true) }
|
||||
scope :staff, -> { admins.or(moderators) }
|
||||
end
|
||||
|
||||
def staff?
|
||||
admin? || moderator?
|
||||
end
|
||||
|
||||
def role=(value)
|
||||
case value
|
||||
when 'admin'
|
||||
self.admin = true
|
||||
self.moderator = false
|
||||
when 'moderator'
|
||||
self.admin = false
|
||||
self.moderator = true
|
||||
else
|
||||
self.admin = false
|
||||
self.moderator = false
|
||||
end
|
||||
end
|
||||
|
||||
def role
|
||||
if admin?
|
||||
'admin'
|
||||
elsif moderator?
|
||||
'moderator'
|
||||
else
|
||||
'user'
|
||||
end
|
||||
end
|
||||
|
||||
def role?(role)
|
||||
case role
|
||||
when 'user'
|
||||
true
|
||||
when 'moderator'
|
||||
staff?
|
||||
when 'admin'
|
||||
admin?
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def promote!
|
||||
if moderator?
|
||||
update!(moderator: false, admin: true)
|
||||
elsif !admin?
|
||||
update!(moderator: true)
|
||||
end
|
||||
end
|
||||
|
||||
def demote!
|
||||
if admin?
|
||||
update!(admin: false, moderator: true)
|
||||
elsif moderator?
|
||||
update!(moderator: false)
|
||||
end
|
||||
end
|
||||
end
|
@ -15,10 +15,8 @@ class Form::AdminSettings
|
||||
closed_registrations_message
|
||||
open_deletion
|
||||
timeline_preview
|
||||
show_staff_badge
|
||||
bootstrap_timeline_accounts
|
||||
theme
|
||||
min_invite_role
|
||||
activity_api_enabled
|
||||
peers_api_enabled
|
||||
show_known_fediverse_at_about_page
|
||||
@ -39,7 +37,6 @@ class Form::AdminSettings
|
||||
BOOLEAN_KEYS = %i(
|
||||
open_deletion
|
||||
timeline_preview
|
||||
show_staff_badge
|
||||
activity_api_enabled
|
||||
peers_api_enabled
|
||||
show_known_fediverse_at_about_page
|
||||
@ -62,7 +59,6 @@ class Form::AdminSettings
|
||||
validates :site_short_description, :site_description, html: { wrap_with: :p }
|
||||
validates :site_extended_description, :site_terms, :closed_registrations_message, html: true
|
||||
validates :registrations_mode, inclusion: { in: %w(open approved none) }
|
||||
validates :min_invite_role, inclusion: { in: %w(disabled user moderator admin) }
|
||||
validates :site_contact_email, :site_contact_username, presence: true
|
||||
validates :site_contact_username, existing_username: true
|
||||
validates :bootstrap_timeline_accounts, existing_username: { multiple: true }
|
||||
|
@ -34,7 +34,7 @@ module Trends
|
||||
|
||||
return if links_requiring_review.empty? && tags_requiring_review.empty? && statuses_requiring_review.empty?
|
||||
|
||||
User.staff.includes(:account).find_each do |user|
|
||||
User.those_who_can(:manage_taxonomies).includes(:account).find_each do |user|
|
||||
AdminMailer.new_trends(user.account, links_requiring_review, tags_requiring_review, statuses_requiring_review).deliver_later! if user.allows_trends_review_emails?
|
||||
end
|
||||
end
|
||||
|
@ -37,6 +37,7 @@
|
||||
# sign_in_token_sent_at :datetime
|
||||
# webauthn_id :string
|
||||
# sign_up_ip :inet
|
||||
# role_id :bigint(8)
|
||||
#
|
||||
|
||||
class User < ApplicationRecord
|
||||
@ -50,7 +51,6 @@ class User < ApplicationRecord
|
||||
)
|
||||
|
||||
include Settings::Extend
|
||||
include UserRoles
|
||||
include Redisable
|
||||
include LanguagesHelper
|
||||
|
||||
@ -79,6 +79,7 @@ class User < ApplicationRecord
|
||||
belongs_to :account, inverse_of: :user
|
||||
belongs_to :invite, counter_cache: :uses, optional: true
|
||||
belongs_to :created_by_application, class_name: 'Doorkeeper::Application', optional: true
|
||||
belongs_to :role, class_name: 'UserRole', optional: true
|
||||
accepts_nested_attributes_for :account
|
||||
|
||||
has_many :applications, class_name: 'Doorkeeper::Application', as: :owner
|
||||
@ -103,6 +104,7 @@ class User < ApplicationRecord
|
||||
validates_with RegistrationFormTimeValidator, on: :create
|
||||
validates :website, absence: true, on: :create
|
||||
validates :confirm_password, absence: true, on: :create
|
||||
validate :validate_role_elevation
|
||||
|
||||
scope :recent, -> { order(id: :desc) }
|
||||
scope :pending, -> { where(approved: false) }
|
||||
@ -117,6 +119,7 @@ class User < ApplicationRecord
|
||||
scope :emailable, -> { confirmed.enabled.joins(:account).merge(Account.searchable) }
|
||||
|
||||
before_validation :sanitize_languages
|
||||
before_validation :sanitize_role
|
||||
before_create :set_approved
|
||||
after_commit :send_pending_devise_notifications
|
||||
after_create_commit :trigger_webhooks
|
||||
@ -135,8 +138,28 @@ class User < ApplicationRecord
|
||||
:disable_swiping, :always_send_emails,
|
||||
to: :settings, prefix: :setting, allow_nil: false
|
||||
|
||||
delegate :can?, to: :role
|
||||
|
||||
attr_reader :invite_code
|
||||
attr_writer :external, :bypass_invite_request_check
|
||||
attr_writer :external, :bypass_invite_request_check, :current_account
|
||||
|
||||
def self.those_who_can(*any_of_privileges)
|
||||
matching_role_ids = UserRole.that_can(*any_of_privileges).map(&:id)
|
||||
|
||||
if matching_role_ids.empty?
|
||||
none
|
||||
else
|
||||
where(role_id: matching_role_ids)
|
||||
end
|
||||
end
|
||||
|
||||
def role
|
||||
if role_id.nil?
|
||||
UserRole.everyone
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def confirmed?
|
||||
confirmed_at.present?
|
||||
@ -441,6 +464,11 @@ class User < ApplicationRecord
|
||||
self.chosen_languages = nil if chosen_languages.empty?
|
||||
end
|
||||
|
||||
def sanitize_role
|
||||
return if role.nil?
|
||||
self.role = nil if role.everyone?
|
||||
end
|
||||
|
||||
def prepare_new_user!
|
||||
BootstrapTimelineWorker.perform_async(account_id)
|
||||
ActivityTracker.increment('activity:accounts:local')
|
||||
@ -453,7 +481,7 @@ class User < ApplicationRecord
|
||||
end
|
||||
|
||||
def notify_staff_about_pending_account!
|
||||
User.staff.includes(:account).find_each do |u|
|
||||
User.those_who_can(:manage_users).includes(:account).find_each do |u|
|
||||
next unless u.allows_pending_account_emails?
|
||||
AdminMailer.new_pending_account(u.account, self).deliver_later
|
||||
end
|
||||
@ -471,6 +499,10 @@ class User < ApplicationRecord
|
||||
email_changed? && !external? && !(Rails.env.test? || Rails.env.development?)
|
||||
end
|
||||
|
||||
def validate_role_elevation
|
||||
errors.add(:role_id, :elevated) if defined?(@current_account) && role&.overrides?(@current_account&.user_role)
|
||||
end
|
||||
|
||||
def invite_text_required?
|
||||
Setting.require_invite_text && !invited? && !external? && !bypass_invite_request_check?
|
||||
end
|
||||
|
179
app/models/user_role.rb
Normal file
179
app/models/user_role.rb
Normal file
@ -0,0 +1,179 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: user_roles
|
||||
#
|
||||
# id :bigint(8) not null, primary key
|
||||
# name :string default(""), not null
|
||||
# color :string default(""), not null
|
||||
# position :integer default(0), not null
|
||||
# permissions :bigint(8) default(0), not null
|
||||
# highlighted :boolean default(FALSE), not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
|
||||
class UserRole < ApplicationRecord
|
||||
FLAGS = {
|
||||
administrator: (1 << 0),
|
||||
view_devops: (1 << 1),
|
||||
view_audit_log: (1 << 2),
|
||||
view_dashboard: (1 << 3),
|
||||
manage_reports: (1 << 4),
|
||||
manage_federation: (1 << 5),
|
||||
manage_settings: (1 << 6),
|
||||
manage_blocks: (1 << 7),
|
||||
manage_taxonomies: (1 << 8),
|
||||
manage_appeals: (1 << 9),
|
||||
manage_users: (1 << 10),
|
||||
manage_invites: (1 << 11),
|
||||
manage_rules: (1 << 12),
|
||||
manage_announcements: (1 << 13),
|
||||
manage_custom_emojis: (1 << 14),
|
||||
manage_webhooks: (1 << 15),
|
||||
invite_users: (1 << 16),
|
||||
manage_roles: (1 << 17),
|
||||
manage_user_access: (1 << 18),
|
||||
delete_user_data: (1 << 19),
|
||||
}.freeze
|
||||
|
||||
module Flags
|
||||
NONE = 0
|
||||
ALL = FLAGS.values.reduce(&:|)
|
||||
|
||||
DEFAULT = FLAGS[:invite_users]
|
||||
|
||||
CATEGORIES = {
|
||||
invites: %i(
|
||||
invite_users
|
||||
).freeze,
|
||||
|
||||
moderation: %w(
|
||||
view_dashboard
|
||||
view_audit_log
|
||||
manage_users
|
||||
manage_user_access
|
||||
delete_user_data
|
||||
manage_reports
|
||||
manage_appeals
|
||||
manage_federation
|
||||
manage_blocks
|
||||
manage_taxonomies
|
||||
manage_invites
|
||||
).freeze,
|
||||
|
||||
administration: %w(
|
||||
manage_settings
|
||||
manage_rules
|
||||
manage_roles
|
||||
manage_webhooks
|
||||
manage_custom_emojis
|
||||
manage_announcements
|
||||
).freeze,
|
||||
|
||||
devops: %w(
|
||||
view_devops
|
||||
).freeze,
|
||||
|
||||
special: %i(
|
||||
administrator
|
||||
).freeze,
|
||||
}.freeze
|
||||
end
|
||||
|
||||
attr_writer :current_account
|
||||
|
||||
validates :name, presence: true, unless: :everyone?
|
||||
validates :color, format: { with: /\A#?(?:[A-F0-9]{3}){1,2}\z/i }, unless: -> { color.blank? }
|
||||
|
||||
validate :validate_permissions_elevation
|
||||
validate :validate_position_elevation
|
||||
validate :validate_dangerous_permissions
|
||||
|
||||
before_validation :set_position
|
||||
|
||||
scope :assignable, -> { where.not(id: -99).order(position: :asc) }
|
||||
|
||||
has_many :users, inverse_of: :role, foreign_key: 'role_id', dependent: :nullify
|
||||
|
||||
def self.nobody
|
||||
@nobody ||= UserRole.new(permissions: Flags::NONE, position: -1)
|
||||
end
|
||||
|
||||
def self.everyone
|
||||
UserRole.find(-99)
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
UserRole.create!(id: -99, permissions: Flags::DEFAULT)
|
||||
end
|
||||
|
||||
def self.that_can(*any_of_privileges)
|
||||
all.select { |role| role.can?(*any_of_privileges) }
|
||||
end
|
||||
|
||||
def everyone?
|
||||
id == -99
|
||||
end
|
||||
|
||||
def nobody?
|
||||
id.nil?
|
||||
end
|
||||
|
||||
def permissions_as_keys
|
||||
FLAGS.keys.select { |privilege| permissions & FLAGS[privilege] == FLAGS[privilege] }.map(&:to_s)
|
||||
end
|
||||
|
||||
def permissions_as_keys=(value)
|
||||
self.permissions = value.map(&:presence).compact.reduce(Flags::NONE) { |bitmask, privilege| FLAGS.key?(privilege.to_sym) ? (bitmask | FLAGS[privilege.to_sym]) : bitmask }
|
||||
end
|
||||
|
||||
def can?(*any_of_privileges)
|
||||
any_of_privileges.any? { |privilege| in_permissions?(privilege) }
|
||||
end
|
||||
|
||||
def overrides?(other_role)
|
||||
other_role.nil? || position > other_role.position
|
||||
end
|
||||
|
||||
def computed_permissions
|
||||
# If called on the everyone role, no further computation needed
|
||||
return permissions if everyone?
|
||||
|
||||
# If called on the nobody role, no permissions are there to be given
|
||||
return Flags::NONE if nobody?
|
||||
|
||||
# Otherwise, compute permissions based on special conditions
|
||||
@computed_permissions ||= begin
|
||||
permissions = self.class.everyone.permissions | self.permissions
|
||||
|
||||
if permissions & FLAGS[:administrator] == FLAGS[:administrator]
|
||||
Flags::ALL
|
||||
else
|
||||
permissions
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def in_permissions?(privilege)
|
||||
raise ArgumentError, "Unknown privilege: #{privilege}" unless FLAGS.key?(privilege)
|
||||
computed_permissions & FLAGS[privilege] == FLAGS[privilege]
|
||||
end
|
||||
|
||||
def set_position
|
||||
self.position = -1 if everyone?
|
||||
end
|
||||
|
||||
def validate_permissions_elevation
|
||||
errors.add(:permissions_as_keys, :elevated) if defined?(@current_account) && @current_account.user_role.computed_permissions & permissions != permissions
|
||||
end
|
||||
|
||||
def validate_position_elevation
|
||||
errors.add(:position, :elevated) if defined?(@current_account) && @current_account.user_role.position < position
|
||||
end
|
||||
|
||||
def validate_dangerous_permissions
|
||||
errors.add(:permissions_as_keys, :dangerous) if everyone? && Flags::DEFAULT & permissions != permissions
|
||||
end
|
||||
end
|
@ -2,11 +2,11 @@
|
||||
|
||||
class AccountModerationNotePolicy < ApplicationPolicy
|
||||
def create?
|
||||
staff?
|
||||
role.can?(:manage_reports)
|
||||
end
|
||||
|
||||
def destroy?
|
||||
admin? || owner?
|
||||
owner? || (role.can?(:manage_reports) && role.overrides?(record.account.user_role))
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -2,74 +2,66 @@
|
||||
|
||||
class AccountPolicy < ApplicationPolicy
|
||||
def index?
|
||||
staff?
|
||||
role.can?(:manage_users)
|
||||
end
|
||||
|
||||
def show?
|
||||
staff?
|
||||
role.can?(:manage_users)
|
||||
end
|
||||
|
||||
def warn?
|
||||
staff? && !record.user&.staff?
|
||||
role.can?(:manage_users, :manage_reports) && role.overrides?(record.user_role)
|
||||
end
|
||||
|
||||
def suspend?
|
||||
staff? && !record.user&.staff? && !record.instance_actor?
|
||||
role.can?(:manage_users, :manage_reports) && role.overrides?(record.user_role) && !record.instance_actor?
|
||||
end
|
||||
|
||||
def destroy?
|
||||
record.suspended_temporarily? && admin?
|
||||
record.suspended_temporarily? && role.can?(:delete_user_data)
|
||||
end
|
||||
|
||||
def unsuspend?
|
||||
staff? && record.suspension_origin_local?
|
||||
role.can?(:manage_users) && record.suspension_origin_local?
|
||||
end
|
||||
|
||||
def sensitive?
|
||||
staff? && !record.user&.staff?
|
||||
role.can?(:manage_users, :manage_reports) && role.overrides?(record.user_role)
|
||||
end
|
||||
|
||||
def unsensitive?
|
||||
staff?
|
||||
role.can?(:manage_users)
|
||||
end
|
||||
|
||||
def silence?
|
||||
staff? && !record.user&.staff?
|
||||
role.can?(:manage_users, :manage_reports) && role.overrides?(record.user_role)
|
||||
end
|
||||
|
||||
def unsilence?
|
||||
staff?
|
||||
role.can?(:manage_users)
|
||||
end
|
||||
|
||||
def redownload?
|
||||
admin?
|
||||
role.can?(:manage_federation)
|
||||
end
|
||||
|
||||
def remove_avatar?
|
||||
staff?
|
||||
role.can?(:manage_users, :manage_reports) && role.overrides?(record.user_role)
|
||||
end
|
||||
|
||||
def remove_header?
|
||||
staff?
|
||||
end
|
||||
|
||||
def subscribe?
|
||||
admin?
|
||||
end
|
||||
|
||||
def unsubscribe?
|
||||
admin?
|
||||
role.can?(:manage_users, :manage_reports) && role.overrides?(record.user_role)
|
||||
end
|
||||
|
||||
def memorialize?
|
||||
admin? && !record.user&.admin? && !record.instance_actor?
|
||||
role.can?(:delete_user_data) && role.overrides?(record.user_role) && !record.instance_actor?
|
||||
end
|
||||
|
||||
def unblock_email?
|
||||
staff?
|
||||
role.can?(:manage_users)
|
||||
end
|
||||
|
||||
def review?
|
||||
staff?
|
||||
role.can?(:manage_taxonomies)
|
||||
end
|
||||
end
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
class AccountWarningPolicy < ApplicationPolicy
|
||||
def show?
|
||||
target? || staff?
|
||||
target? || role.can?(:manage_appeals)
|
||||
end
|
||||
|
||||
def appeal?
|
||||
|
@ -2,18 +2,18 @@
|
||||
|
||||
class AccountWarningPresetPolicy < ApplicationPolicy
|
||||
def index?
|
||||
staff?
|
||||
role.can?(:manage_settings)
|
||||
end
|
||||
|
||||
def create?
|
||||
staff?
|
||||
role.can?(:manage_settings)
|
||||
end
|
||||
|
||||
def update?
|
||||
staff?
|
||||
role.can?(:manage_settings)
|
||||
end
|
||||
|
||||
def destroy?
|
||||
staff?
|
||||
role.can?(:manage_settings)
|
||||
end
|
||||
end
|
||||
|
@ -2,18 +2,18 @@
|
||||
|
||||
class AnnouncementPolicy < ApplicationPolicy
|
||||
def index?
|
||||
staff?
|
||||
role.can?(:manage_announcements)
|
||||
end
|
||||
|
||||
def create?
|
||||
admin?
|
||||
role.can?(:manage_announcements)
|
||||
end
|
||||
|
||||
def update?
|
||||
admin?
|
||||
role.can?(:manage_announcements)
|
||||
end
|
||||
|
||||
def destroy?
|
||||
admin?
|
||||
role.can?(:manage_announcements)
|
||||
end
|
||||
end
|
||||
|
@ -2,12 +2,14 @@
|
||||
|
||||
class AppealPolicy < ApplicationPolicy
|
||||
def index?
|
||||
staff?
|
||||
role.can?(:manage_appeals)
|
||||
end
|
||||
|
||||
def approve?
|
||||
record.pending? && staff?
|
||||
record.pending? && role.can?(:manage_appeals)
|
||||
end
|
||||
|
||||
alias reject? approve?
|
||||
def reject?
|
||||
record.pending? && role.can?(:manage_appeals)
|
||||
end
|
||||
end
|
||||
|
@ -8,8 +8,6 @@ class ApplicationPolicy
|
||||
@record = record
|
||||
end
|
||||
|
||||
delegate :admin?, :moderator?, :staff?, to: :current_user, allow_nil: true
|
||||
|
||||
private
|
||||
|
||||
def current_user
|
||||
@ -19,4 +17,8 @@ class ApplicationPolicy
|
||||
def user_signed_in?
|
||||
!current_user.nil?
|
||||
end
|
||||
|
||||
def role
|
||||
current_user&.role || UserRole.nobody
|
||||
end
|
||||
end
|
||||
|
7
app/policies/audit_log_policy.rb
Normal file
7
app/policies/audit_log_policy.rb
Normal file
@ -0,0 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class AuditLogPolicy < ApplicationPolicy
|
||||
def index?
|
||||
role.can?(:view_audit_log)
|
||||
end
|
||||
end
|
@ -2,30 +2,30 @@
|
||||
|
||||
class CustomEmojiPolicy < ApplicationPolicy
|
||||
def index?
|
||||
staff?
|
||||
role.can?(:manage_custom_emojis)
|
||||
end
|
||||
|
||||
def create?
|
||||
admin?
|
||||
role.can?(:manage_custom_emojis)
|
||||
end
|
||||
|
||||
def update?
|
||||
admin?
|
||||
role.can?(:manage_custom_emojis)
|
||||
end
|
||||
|
||||
def copy?
|
||||
admin?
|
||||
role.can?(:manage_custom_emojis)
|
||||
end
|
||||
|
||||
def enable?
|
||||
staff?
|
||||
role.can?(:manage_custom_emojis)
|
||||
end
|
||||
|
||||
def disable?
|
||||
staff?
|
||||
role.can?(:manage_custom_emojis)
|
||||
end
|
||||
|
||||
def destroy?
|
||||
admin?
|
||||
role.can?(:manage_custom_emojis)
|
||||
end
|
||||
end
|
||||
|
7
app/policies/dashboard_policy.rb
Normal file
7
app/policies/dashboard_policy.rb
Normal file
@ -0,0 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class DashboardPolicy < ApplicationPolicy
|
||||
def index?
|
||||
role.can?(:view_dashboard)
|
||||
end
|
||||
end
|
@ -2,14 +2,14 @@
|
||||
|
||||
class DeliveryPolicy < ApplicationPolicy
|
||||
def clear_delivery_errors?
|
||||
admin?
|
||||
role.can?(:manage_federation)
|
||||
end
|
||||
|
||||
def restart_delivery?
|
||||
admin?
|
||||
role.can?(:manage_federation)
|
||||
end
|
||||
|
||||
def stop_delivery?
|
||||
admin?
|
||||
role.can?(:manage_federation)
|
||||
end
|
||||
end
|
||||
|
@ -2,18 +2,18 @@
|
||||
|
||||
class DomainAllowPolicy < ApplicationPolicy
|
||||
def index?
|
||||
admin?
|
||||
role.can?(:manage_federation)
|
||||
end
|
||||
|
||||
def show?
|
||||
admin?
|
||||
role.can?(:manage_federation)
|
||||
end
|
||||
|
||||
def create?
|
||||
admin?
|
||||
role.can?(:manage_federation)
|
||||
end
|
||||
|
||||
def destroy?
|
||||
admin?
|
||||
role.can?(:manage_federation)
|
||||
end
|
||||
end
|
||||
|
@ -2,22 +2,22 @@
|
||||
|
||||
class DomainBlockPolicy < ApplicationPolicy
|
||||
def index?
|
||||
admin?
|
||||
role.can?(:manage_federation)
|
||||
end
|
||||
|
||||
def show?
|
||||
admin?
|
||||
role.can?(:manage_federation)
|
||||
end
|
||||
|
||||
def create?
|
||||
admin?
|
||||
role.can?(:manage_federation)
|
||||
end
|
||||
|
||||
def update?
|
||||
admin?
|
||||
role.can?(:manage_federation)
|
||||
end
|
||||
|
||||
def destroy?
|
||||
admin?
|
||||
role.can?(:manage_federation)
|
||||
end
|
||||
end
|
||||
|
@ -2,14 +2,14 @@
|
||||
|
||||
class EmailDomainBlockPolicy < ApplicationPolicy
|
||||
def index?
|
||||
admin?
|
||||
role.can?(:manage_blocks)
|
||||
end
|
||||
|
||||
def create?
|
||||
admin?
|
||||
role.can?(:manage_blocks)
|
||||
end
|
||||
|
||||
def destroy?
|
||||
admin?
|
||||
role.can?(:manage_blocks)
|
||||
end
|
||||
end
|
||||
|
@ -2,14 +2,14 @@
|
||||
|
||||
class FollowRecommendationPolicy < ApplicationPolicy
|
||||
def show?
|
||||
staff?
|
||||
role.can?(:manage_taxonomies)
|
||||
end
|
||||
|
||||
def suppress?
|
||||
staff?
|
||||
role.can?(:manage_taxonomies)
|
||||
end
|
||||
|
||||
def unsuppress?
|
||||
staff?
|
||||
role.can?(:manage_taxonomies)
|
||||
end
|
||||
end
|
||||
|
@ -2,14 +2,14 @@
|
||||
|
||||
class InstancePolicy < ApplicationPolicy
|
||||
def index?
|
||||
admin?
|
||||
role.can?(:manage_federation)
|
||||
end
|
||||
|
||||
def show?
|
||||
admin?
|
||||
role.can?(:manage_federation)
|
||||
end
|
||||
|
||||
def destroy?
|
||||
admin?
|
||||
role.can?(:manage_federation)
|
||||
end
|
||||
end
|
||||
|
@ -2,19 +2,19 @@
|
||||
|
||||
class InvitePolicy < ApplicationPolicy
|
||||
def index?
|
||||
staff?
|
||||
role.can?(:manage_invites)
|
||||
end
|
||||
|
||||
def create?
|
||||
min_required_role?
|
||||
role.can?(:invite_users)
|
||||
end
|
||||
|
||||
def deactivate_all?
|
||||
admin?
|
||||
role.can?(:manage_invites)
|
||||
end
|
||||
|
||||
def destroy?
|
||||
owner? || (Setting.min_invite_role == 'admin' ? admin? : staff?)
|
||||
owner? || role.can?(:manage_invites)
|
||||
end
|
||||
|
||||
private
|
||||
@ -22,8 +22,4 @@ class InvitePolicy < ApplicationPolicy
|
||||
def owner?
|
||||
record.user_id == current_user&.id
|
||||
end
|
||||
|
||||
def min_required_role?
|
||||
current_user&.role?(Setting.min_invite_role)
|
||||
end
|
||||
end
|
||||
|
@ -2,14 +2,14 @@
|
||||
|
||||
class IpBlockPolicy < ApplicationPolicy
|
||||
def index?
|
||||
admin?
|
||||
role.can?(:manage_blocks)
|
||||
end
|
||||
|
||||
def create?
|
||||
admin?
|
||||
role.can?(:manage_blocks)
|
||||
end
|
||||
|
||||
def destroy?
|
||||
admin?
|
||||
role.can?(:manage_blocks)
|
||||
end
|
||||
end
|
||||
|
@ -2,10 +2,10 @@
|
||||
|
||||
class PreviewCardPolicy < ApplicationPolicy
|
||||
def index?
|
||||
staff?
|
||||
role.can?(:manage_taxonomies)
|
||||
end
|
||||
|
||||
def review?
|
||||
staff?
|
||||
role.can?(:manage_taxonomies)
|
||||
end
|
||||
end
|
||||
|
@ -2,10 +2,10 @@
|
||||
|
||||
class PreviewCardProviderPolicy < ApplicationPolicy
|
||||
def index?
|
||||
staff?
|
||||
role.can?(:manage_taxonomies)
|
||||
end
|
||||
|
||||
def review?
|
||||
staff?
|
||||
role.can?(:manage_taxonomies)
|
||||
end
|
||||
end
|
||||
|
@ -2,6 +2,6 @@
|
||||
|
||||
class RelayPolicy < ApplicationPolicy
|
||||
def update?
|
||||
admin?
|
||||
role.can?(:manage_federation)
|
||||
end
|
||||
end
|
||||
|
@ -2,11 +2,11 @@
|
||||
|
||||
class ReportNotePolicy < ApplicationPolicy
|
||||
def create?
|
||||
staff?
|
||||
role.can?(:manage_reports)
|
||||
end
|
||||
|
||||
def destroy?
|
||||
admin? || owner?
|
||||
owner? || (role.can?(:manage_reports) && role.overrides?(record.account.user_role))
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -2,14 +2,14 @@
|
||||
|
||||
class ReportPolicy < ApplicationPolicy
|
||||
def update?
|
||||
staff?
|
||||
role.can?(:manage_reports)
|
||||
end
|
||||
|
||||
def index?
|
||||
staff?
|
||||
role.can?(:manage_reports)
|
||||
end
|
||||
|
||||
def show?
|
||||
staff?
|
||||
role.can?(:manage_reports)
|
||||
end
|
||||
end
|
||||
|
@ -2,18 +2,18 @@
|
||||
|
||||
class RulePolicy < ApplicationPolicy
|
||||
def index?
|
||||
staff?
|
||||
role.can?(:manage_rules)
|
||||
end
|
||||
|
||||
def create?
|
||||
admin?
|
||||
role.can?(:manage_rules)
|
||||
end
|
||||
|
||||
def update?
|
||||
admin?
|
||||
role.can?(:manage_rules)
|
||||
end
|
||||
|
||||
def destroy?
|
||||
admin?
|
||||
role.can?(:manage_rules)
|
||||
end
|
||||
end
|
||||
|
@ -2,14 +2,14 @@
|
||||
|
||||
class SettingsPolicy < ApplicationPolicy
|
||||
def update?
|
||||
admin?
|
||||
role.can?(:manage_settings)
|
||||
end
|
||||
|
||||
def show?
|
||||
admin?
|
||||
role.can?(:manage_settings)
|
||||
end
|
||||
|
||||
def destroy?
|
||||
admin?
|
||||
role.can?(:manage_settings)
|
||||
end
|
||||
end
|
||||
|
@ -8,7 +8,7 @@ class StatusPolicy < ApplicationPolicy
|
||||
end
|
||||
|
||||
def index?
|
||||
staff?
|
||||
role.can?(:manage_reports, :manage_users)
|
||||
end
|
||||
|
||||
def show?
|
||||
@ -32,17 +32,17 @@ class StatusPolicy < ApplicationPolicy
|
||||
end
|
||||
|
||||
def destroy?
|
||||
staff? || owned?
|
||||
role.can?(:manage_reports) || owned?
|
||||
end
|
||||
|
||||
alias unreblog? destroy?
|
||||
|
||||
def update?
|
||||
staff? || owned?
|
||||
role.can?(:manage_reports) || owned?
|
||||
end
|
||||
|
||||
def review?
|
||||
staff?
|
||||
role.can?(:manage_taxonomies)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -2,18 +2,18 @@
|
||||
|
||||
class TagPolicy < ApplicationPolicy
|
||||
def index?
|
||||
staff?
|
||||
role.can?(:manage_taxonomies)
|
||||
end
|
||||
|
||||
def show?
|
||||
staff?
|
||||
role.can?(:manage_taxonomies)
|
||||
end
|
||||
|
||||
def update?
|
||||
staff?
|
||||
role.can?(:manage_taxonomies)
|
||||
end
|
||||
|
||||
def review?
|
||||
staff?
|
||||
role.can?(:manage_taxonomies)
|
||||
end
|
||||
end
|
||||
|
@ -2,52 +2,38 @@
|
||||
|
||||
class UserPolicy < ApplicationPolicy
|
||||
def reset_password?
|
||||
staff? && !record.staff?
|
||||
role.can?(:manage_user_access) && role.overrides?(record.role)
|
||||
end
|
||||
|
||||
def change_email?
|
||||
staff? && !record.staff?
|
||||
role.can?(:manage_user_access) && role.overrides?(record.role)
|
||||
end
|
||||
|
||||
def disable_2fa?
|
||||
admin? && !record.staff?
|
||||
role.can?(:manage_user_access) && role.overrides?(record.role)
|
||||
end
|
||||
|
||||
def change_role?
|
||||
role.can?(:manage_roles) && role.overrides?(record.role)
|
||||
end
|
||||
|
||||
def confirm?
|
||||
staff? && !record.confirmed?
|
||||
role.can?(:manage_user_access) && !record.confirmed?
|
||||
end
|
||||
|
||||
def enable?
|
||||
staff?
|
||||
role.can?(:manage_users)
|
||||
end
|
||||
|
||||
def approve?
|
||||
staff? && !record.approved?
|
||||
role.can?(:manage_users) && !record.approved?
|
||||
end
|
||||
|
||||
def reject?
|
||||
staff? && !record.approved?
|
||||
role.can?(:manage_users) && !record.approved?
|
||||
end
|
||||
|
||||
def disable?
|
||||
staff? && !record.admin?
|
||||
end
|
||||
|
||||
def promote?
|
||||
admin? && promotable?
|
||||
end
|
||||
|
||||
def demote?
|
||||
admin? && !record.admin? && demoteable?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def promotable?
|
||||
record.approved? && (!record.staff? || !record.admin?)
|
||||
end
|
||||
|
||||
def demoteable?
|
||||
record.staff?
|
||||
role.can?(:manage_users) && role.overrides?(record.role)
|
||||
end
|
||||
end
|
||||
|
19
app/policies/user_role_policy.rb
Normal file
19
app/policies/user_role_policy.rb
Normal file
@ -0,0 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class UserRolePolicy < ApplicationPolicy
|
||||
def index?
|
||||
role.can?(:manage_roles)
|
||||
end
|
||||
|
||||
def create?
|
||||
role.can?(:manage_roles)
|
||||
end
|
||||
|
||||
def update?
|
||||
role.can?(:manage_roles) && role.overrides?(record)
|
||||
end
|
||||
|
||||
def destroy?
|
||||
!record.everyone? && role.can?(:manage_roles) && role.overrides?(record) && role.id != record.id
|
||||
end
|
||||
end
|
@ -2,34 +2,34 @@
|
||||
|
||||
class WebhookPolicy < ApplicationPolicy
|
||||
def index?
|
||||
admin?
|
||||
role.can?(:manage_webhooks)
|
||||
end
|
||||
|
||||
def create?
|
||||
admin?
|
||||
role.can?(:manage_webhooks)
|
||||
end
|
||||
|
||||
def show?
|
||||
admin?
|
||||
role.can?(:manage_webhooks)
|
||||
end
|
||||
|
||||
def update?
|
||||
admin?
|
||||
role.can?(:manage_webhooks)
|
||||
end
|
||||
|
||||
def enable?
|
||||
admin?
|
||||
role.can?(:manage_webhooks)
|
||||
end
|
||||
|
||||
def disable?
|
||||
admin?
|
||||
role.can?(:manage_webhooks)
|
||||
end
|
||||
|
||||
def rotate_secret?
|
||||
admin?
|
||||
role.can?(:manage_webhooks)
|
||||
end
|
||||
|
||||
def destroy?
|
||||
admin?
|
||||
role.can?(:manage_webhooks)
|
||||
end
|
||||
end
|
||||
|
@ -3,4 +3,8 @@
|
||||
class InitialStatePresenter < ActiveModelSerializers::Model
|
||||
attributes :settings, :push_subscription, :token,
|
||||
:current_account, :admin, :text, :visibility
|
||||
|
||||
def role
|
||||
current_account&.user_role
|
||||
end
|
||||
end
|
||||
|
@ -6,6 +6,7 @@ class InitialStateSerializer < ActiveModel::Serializer
|
||||
:languages
|
||||
|
||||
has_one :push_subscription, serializer: REST::WebPushSubscriptionSerializer
|
||||
has_one :role, serializer: REST::RoleSerializer
|
||||
|
||||
def meta
|
||||
store = {
|
||||
@ -19,7 +20,6 @@ class InitialStateSerializer < ActiveModel::Serializer
|
||||
repository: Mastodon::Version.repository,
|
||||
source_url: Mastodon::Version.source_url,
|
||||
version: Mastodon::Version.to_s,
|
||||
invites_enabled: Setting.min_invite_role == 'user',
|
||||
limited_federation_mode: Rails.configuration.x.whitelist_mode,
|
||||
mascot: instance_presenter.mascot&.file&.url,
|
||||
profile_directory: Setting.profile_directory,
|
||||
@ -39,7 +39,6 @@ class InitialStateSerializer < ActiveModel::Serializer
|
||||
store[:advanced_layout] = object.current_account.user.setting_advanced_layout
|
||||
store[:use_blurhash] = object.current_account.user.setting_use_blurhash
|
||||
store[:use_pending_items] = object.current_account.user.setting_use_pending_items
|
||||
store[:is_staff] = object.current_account.user.staff?
|
||||
store[:trends] = Setting.trends && object.current_account.user.setting_trends
|
||||
store[:crop_images] = object.current_account.user.setting_crop_images
|
||||
else
|
||||
|
@ -3,6 +3,8 @@
|
||||
class REST::CredentialAccountSerializer < REST::AccountSerializer
|
||||
attributes :source
|
||||
|
||||
has_one :role, serializer: REST::RoleSerializer
|
||||
|
||||
def source
|
||||
user = object.user
|
||||
|
||||
@ -15,4 +17,8 @@ class REST::CredentialAccountSerializer < REST::AccountSerializer
|
||||
follow_requests_count: FollowRequest.where(target_account: object).limit(40).count,
|
||||
}
|
||||
end
|
||||
|
||||
def role
|
||||
object.user_role
|
||||
end
|
||||
end
|
||||
|
@ -93,7 +93,7 @@ class REST::InstanceSerializer < ActiveModel::Serializer
|
||||
end
|
||||
|
||||
def invites_enabled
|
||||
Setting.min_invite_role == 'user'
|
||||
UserRole.everyone.can?(:invite_users)
|
||||
end
|
||||
|
||||
private
|
||||
|
13
app/serializers/rest/role_serializer.rb
Normal file
13
app/serializers/rest/role_serializer.rb
Normal file
@ -0,0 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class REST::RoleSerializer < ActiveModel::Serializer
|
||||
attributes :id, :name, :permissions, :color, :highlighted
|
||||
|
||||
def id
|
||||
object.id.to_s
|
||||
end
|
||||
|
||||
def permissions
|
||||
object.computed_permissions.to_s
|
||||
end
|
||||
end
|
@ -61,11 +61,11 @@ class AccountSearchService < BaseService
|
||||
end
|
||||
|
||||
def advanced_search_results
|
||||
Account.advanced_search_for(terms_for_query, account, limit_for_non_exact_results, options[:following], offset)
|
||||
Account.advanced_search_for(terms_for_query, account, limit: limit_for_non_exact_results, following: options[:following], offset: offset)
|
||||
end
|
||||
|
||||
def simple_search_results
|
||||
Account.search_for(terms_for_query, limit_for_non_exact_results, offset)
|
||||
Account.search_for(terms_for_query, limit: limit_for_non_exact_results, offset: offset)
|
||||
end
|
||||
|
||||
def from_elasticsearch
|
||||
|
@ -22,7 +22,7 @@ class AppealService < BaseService
|
||||
end
|
||||
|
||||
def notify_staff!
|
||||
User.staff.includes(:account).each do |u|
|
||||
User.those_who_can(:manage_appeals).includes(:account).each do |u|
|
||||
AdminMailer.new_appeal(u.account, @appeal).deliver_later if u.allows_appeal_emails?
|
||||
end
|
||||
end
|
||||
|
@ -17,7 +17,7 @@ class BootstrapTimelineService < BaseService
|
||||
end
|
||||
|
||||
def notify_staff!
|
||||
User.staff.includes(:account).find_each do |user|
|
||||
User.those_who_can(:manage_users).includes(:account).find_each do |user|
|
||||
LocalNotificationWorker.perform_async(user.account_id, @source_account.id, 'Account', 'admin.sign_up')
|
||||
end
|
||||
end
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user