about summary refs log tree commit diff
path: root/spec
diff options
context:
space:
mode:
authorEugen Rochko <eugen@zeonfederated.com>2022-07-05 02:41:40 +0200
committerGitHub <noreply@github.com>2022-07-05 02:41:40 +0200
commit44b2ee3485ba0845e5910cefcb4b1e2f84f34470 (patch)
treecc91189c9b36aaf0a04d339455c6d238992753a9 /spec
parent1b4054256f9d3302b44f71627a23bb0902578867 (diff)
Add customizable user roles (#18641)
* Add customizable user roles

* Various fixes and improvements

* Add migration for old settings and fix tootctl role management
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/admin/account_moderation_notes_controller_spec.rb2
-rw-r--r--spec/controllers/admin/accounts_controller_spec.rb52
-rw-r--r--spec/controllers/admin/action_logs_controller_spec.rb2
-rw-r--r--spec/controllers/admin/base_controller_spec.rb7
-rw-r--r--spec/controllers/admin/change_email_controller_spec.rb2
-rw-r--r--spec/controllers/admin/confirmations_controller_spec.rb2
-rw-r--r--spec/controllers/admin/custom_emojis_controller_spec.rb2
-rw-r--r--spec/controllers/admin/dashboard_controller_spec.rb2
-rw-r--r--spec/controllers/admin/disputes/appeals_controller_spec.rb4
-rw-r--r--spec/controllers/admin/domain_blocks_controller_spec.rb2
-rw-r--r--spec/controllers/admin/email_domain_blocks_controller_spec.rb2
-rw-r--r--spec/controllers/admin/instances_controller_spec.rb8
-rw-r--r--spec/controllers/admin/invites_controller_spec.rb2
-rw-r--r--spec/controllers/admin/report_notes_controller_spec.rb2
-rw-r--r--spec/controllers/admin/reports_controller_spec.rb2
-rw-r--r--spec/controllers/admin/resets_controller_spec.rb2
-rw-r--r--spec/controllers/admin/roles_controller_spec.rb244
-rw-r--r--spec/controllers/admin/settings_controller_spec.rb2
-rw-r--r--spec/controllers/admin/statuses_controller_spec.rb2
-rw-r--r--spec/controllers/admin/tags_controller_spec.rb2
-rw-r--r--spec/controllers/admin/users/roles_controller.rb81
-rw-r--r--spec/controllers/admin/users/two_factor_authentications_controller_spec.rb (renamed from spec/controllers/admin/two_factor_authentications_controller_spec.rb)5
-rw-r--r--spec/controllers/api/v1/admin/account_actions_controller_spec.rb6
-rw-r--r--spec/controllers/api/v1/admin/accounts_controller_spec.rb20
-rw-r--r--spec/controllers/api/v1/admin/domain_allows_controller_spec.rb20
-rw-r--r--spec/controllers/api/v1/admin/domain_blocks_controller_spec.rb20
-rw-r--r--spec/controllers/api/v1/admin/reports_controller_spec.rb16
-rw-r--r--spec/controllers/api/v1/reports_controller_spec.rb2
-rw-r--r--spec/controllers/api/v2/admin/accounts_controller_spec.rb6
-rw-r--r--spec/controllers/application_controller_spec.rb64
-rw-r--r--spec/controllers/disputes/appeals_controller_spec.rb2
-rw-r--r--spec/controllers/invites_controller_spec.rb40
-rw-r--r--spec/fabricators/user_role_fabricator.rb5
-rw-r--r--spec/models/account_spec.rb14
-rw-r--r--spec/models/admin/account_action_spec.rb2
-rw-r--r--spec/models/user_role_spec.rb189
-rw-r--r--spec/models/user_spec.rb159
-rw-r--r--spec/policies/account_moderation_note_policy_spec.rb4
-rw-r--r--spec/policies/account_policy_spec.rb8
-rw-r--r--spec/policies/custom_emoji_policy_spec.rb2
-rw-r--r--spec/policies/domain_block_policy_spec.rb2
-rw-r--r--spec/policies/email_domain_block_policy_spec.rb2
-rw-r--r--spec/policies/instance_policy_spec.rb2
-rw-r--r--spec/policies/invite_policy_spec.rb54
-rw-r--r--spec/policies/relay_policy_spec.rb2
-rw-r--r--spec/policies/report_note_policy_spec.rb5
-rw-r--r--spec/policies/report_policy_spec.rb2
-rw-r--r--spec/policies/settings_policy_spec.rb2
-rw-r--r--spec/policies/status_policy_spec.rb2
-rw-r--r--spec/policies/tag_policy_spec.rb2
-rw-r--r--spec/policies/user_policy_spec.rb55
51 files changed, 680 insertions, 460 deletions
diff --git a/spec/controllers/admin/account_moderation_notes_controller_spec.rb b/spec/controllers/admin/account_moderation_notes_controller_spec.rb
index 410ce6543..d3f3263f8 100644
--- a/spec/controllers/admin/account_moderation_notes_controller_spec.rb
+++ b/spec/controllers/admin/account_moderation_notes_controller_spec.rb
@@ -3,7 +3,7 @@ require 'rails_helper'
 RSpec.describe Admin::AccountModerationNotesController, type: :controller do
   render_views
 
-  let(:user) { Fabricate(:user, admin: true) }
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
   let(:target_account) { Fabricate(:account) }
 
   before do
diff --git a/spec/controllers/admin/accounts_controller_spec.rb b/spec/controllers/admin/accounts_controller_spec.rb
index 1779fb7c0..1bd51a0c8 100644
--- a/spec/controllers/admin/accounts_controller_spec.rb
+++ b/spec/controllers/admin/accounts_controller_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe Admin::AccountsController, type: :controller do
   before { sign_in current_user, scope: :user }
 
   describe 'GET #index' do
-    let(:current_user) { Fabricate(:user, admin: true) }
+    let(:current_user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
 
     around do |example|
       default_per_page = Account.default_per_page
@@ -60,7 +60,7 @@ RSpec.describe Admin::AccountsController, type: :controller do
   end
 
   describe 'GET #show' do
-    let(:current_user) { Fabricate(:user, admin: true) }
+    let(:current_user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
     let(:account) { Fabricate(:account) }
 
     it 'returns http success' do
@@ -72,15 +72,15 @@ RSpec.describe Admin::AccountsController, type: :controller do
   describe 'POST #memorialize' do
     subject { post :memorialize, params: { id: account.id } }
 
-    let(:current_user) { Fabricate(:user, admin: current_user_admin) }
+    let(:current_user) { Fabricate(:user, role: current_role) }
     let(:account) { user.account }
-    let(:user) { Fabricate(:user, admin: target_user_admin) }
+    let(:user) { Fabricate(:user, role: target_role) }
 
     context 'when user is admin' do
-      let(:current_user_admin) { true }
+      let(:current_role) { UserRole.find_by(name: 'Admin') }
 
       context 'when target user is admin' do
-        let(:target_user_admin) { true }
+        let(:target_role) { UserRole.find_by(name: 'Admin') }
 
         it 'fails to memorialize account' do
           is_expected.to have_http_status :forbidden
@@ -89,7 +89,7 @@ RSpec.describe Admin::AccountsController, type: :controller do
       end
 
       context 'when target user is not admin' do
-        let(:target_user_admin) { false }
+        let(:target_role) { UserRole.find_by(name: 'Moderator') }
 
         it 'succeeds in memorializing account' do
           is_expected.to redirect_to admin_account_path(account.id)
@@ -99,10 +99,10 @@ RSpec.describe Admin::AccountsController, type: :controller do
     end
 
     context 'when user is not admin' do
-      let(:current_user_admin) { false }
+      let(:current_role) { UserRole.find_by(name: 'Moderator') }
 
       context 'when target user is admin' do
-        let(:target_user_admin) { true }
+        let(:target_role) { UserRole.find_by(name: 'Admin') }
 
         it 'fails to memorialize account' do
           is_expected.to have_http_status :forbidden
@@ -111,7 +111,7 @@ RSpec.describe Admin::AccountsController, type: :controller do
       end
 
       context 'when target user is not admin' do
-        let(:target_user_admin) { false }
+        let(:target_role) { UserRole.find_by(name: 'Moderator') }
 
         it 'fails to memorialize account' do
           is_expected.to have_http_status :forbidden
@@ -124,12 +124,12 @@ RSpec.describe Admin::AccountsController, type: :controller do
   describe 'POST #enable' do
     subject { post :enable, params: { id: account.id } }
 
-    let(:current_user) { Fabricate(:user, admin: admin) }
+    let(:current_user) { Fabricate(:user, role: role) }
     let(:account) { user.account }
     let(:user) { Fabricate(:user, disabled: true) }
 
     context 'when user is admin' do
-      let(:admin) { true }
+      let(:role) { UserRole.find_by(name: 'Admin') }
 
       it 'succeeds in enabling account' do
         is_expected.to redirect_to admin_account_path(account.id)
@@ -138,7 +138,7 @@ RSpec.describe Admin::AccountsController, type: :controller do
     end
 
     context 'when user is not admin' do
-      let(:admin) { false }
+      let(:role) { UserRole.everyone }
 
       it 'fails to enable account' do
         is_expected.to have_http_status :forbidden
@@ -150,19 +150,23 @@ RSpec.describe Admin::AccountsController, type: :controller do
   describe 'POST #redownload' do
     subject { post :redownload, params: { id: account.id } }
 
-    let(:current_user) { Fabricate(:user, admin: admin) }
-    let(:account) { Fabricate(:account) }
+    let(:current_user) { Fabricate(:user, role: role) }
+    let(:account) { Fabricate(:account, domain: 'example.com') }
+
+    before do
+      allow_any_instance_of(ResolveAccountService).to receive(:call)
+    end
 
     context 'when user is admin' do
-      let(:admin) { true }
+      let(:role) { UserRole.find_by(name: 'Admin') }
 
-      it 'succeeds in redownloadin' do
+      it 'succeeds in redownloading' do
         is_expected.to redirect_to admin_account_path(account.id)
       end
     end
 
     context 'when user is not admin' do
-      let(:admin) { false }
+      let(:role) { UserRole.everyone }
 
       it 'fails to redownload' do
         is_expected.to have_http_status :forbidden
@@ -173,11 +177,11 @@ RSpec.describe Admin::AccountsController, type: :controller do
   describe 'POST #remove_avatar' do
     subject { post :remove_avatar, params: { id: account.id } }
 
-    let(:current_user) { Fabricate(:user, admin: admin) }
+    let(:current_user) { Fabricate(:user, role: role) }
     let(:account) { Fabricate(:account) }
 
     context 'when user is admin' do
-      let(:admin) { true }
+      let(:role) { UserRole.find_by(name: 'Admin') }
 
       it 'succeeds in removing avatar' do
         is_expected.to redirect_to admin_account_path(account.id)
@@ -185,7 +189,7 @@ RSpec.describe Admin::AccountsController, type: :controller do
     end
 
     context 'when user is not admin' do
-      let(:admin) { false }
+      let(:role) { UserRole.everyone }
 
       it 'fails to remove avatar' do
         is_expected.to have_http_status :forbidden
@@ -196,12 +200,12 @@ RSpec.describe Admin::AccountsController, type: :controller do
   describe 'POST #unblock_email' do
     subject { post :unblock_email, params: { id: account.id } }
 
-    let(:current_user) { Fabricate(:user, admin: admin) }
+    let(:current_user) { Fabricate(:user, role: role) }
     let(:account) { Fabricate(:account, suspended: true) }
     let!(:email_block) { Fabricate(:canonical_email_block, reference_account: account) }
 
     context 'when user is admin' do
-      let(:admin) { true }
+      let(:role) { UserRole.find_by(name: 'Admin') }
 
       it 'succeeds in removing email blocks' do
         expect { subject }.to change { CanonicalEmailBlock.where(reference_account: account).count }.from(1).to(0)
@@ -214,7 +218,7 @@ RSpec.describe Admin::AccountsController, type: :controller do
     end
 
     context 'when user is not admin' do
-      let(:admin) { false }
+      let(:role) { UserRole.everyone }
 
       it 'fails to remove avatar' do
         subject
diff --git a/spec/controllers/admin/action_logs_controller_spec.rb b/spec/controllers/admin/action_logs_controller_spec.rb
index 4720ed2e2..c1957258f 100644
--- a/spec/controllers/admin/action_logs_controller_spec.rb
+++ b/spec/controllers/admin/action_logs_controller_spec.rb
@@ -5,7 +5,7 @@ require 'rails_helper'
 describe Admin::ActionLogsController, type: :controller do
   describe 'GET #index' do
     it 'returns 200' do
-      sign_in Fabricate(:user, admin: true)
+      sign_in Fabricate(:user, role: UserRole.find_by(name: 'Admin'))
       get :index, params: { page: 1 }
 
       expect(response).to have_http_status(200)
diff --git a/spec/controllers/admin/base_controller_spec.rb b/spec/controllers/admin/base_controller_spec.rb
index 9ac833623..44be91951 100644
--- a/spec/controllers/admin/base_controller_spec.rb
+++ b/spec/controllers/admin/base_controller_spec.rb
@@ -5,13 +5,14 @@ require 'rails_helper'
 describe Admin::BaseController, type: :controller do
   controller do
     def success
+      authorize :dashboard, :index?
       render 'admin/reports/show'
     end
   end
 
   it 'requires administrator or moderator' do
     routes.draw { get 'success' => 'admin/base#success' }
-    sign_in(Fabricate(:user, admin: false, moderator: false))
+    sign_in(Fabricate(:user))
     get :success
 
     expect(response).to have_http_status(:forbidden)
@@ -19,14 +20,14 @@ describe Admin::BaseController, type: :controller do
 
   it 'renders admin layout as a moderator' do
     routes.draw { get 'success' => 'admin/base#success' }
-    sign_in(Fabricate(:user, moderator: true))
+    sign_in(Fabricate(:user, role: UserRole.find_by(name: 'Moderator')))
     get :success
     expect(response).to render_template layout: 'admin'
   end
 
   it 'renders admin layout as an admin' do
     routes.draw { get 'success' => 'admin/base#success' }
-    sign_in(Fabricate(:user, admin: true))
+    sign_in(Fabricate(:user, role: UserRole.find_by(name: 'Admin')))
     get :success
     expect(response).to render_template layout: 'admin'
   end
diff --git a/spec/controllers/admin/change_email_controller_spec.rb b/spec/controllers/admin/change_email_controller_spec.rb
index e7f3f7c97..cf8a27d39 100644
--- a/spec/controllers/admin/change_email_controller_spec.rb
+++ b/spec/controllers/admin/change_email_controller_spec.rb
@@ -3,7 +3,7 @@ require 'rails_helper'
 RSpec.describe Admin::ChangeEmailsController, type: :controller do
   render_views
 
-  let(:admin) { Fabricate(:user, admin: true) }
+  let(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
 
   before do
     sign_in admin
diff --git a/spec/controllers/admin/confirmations_controller_spec.rb b/spec/controllers/admin/confirmations_controller_spec.rb
index 5b4f7e925..6268903c4 100644
--- a/spec/controllers/admin/confirmations_controller_spec.rb
+++ b/spec/controllers/admin/confirmations_controller_spec.rb
@@ -4,7 +4,7 @@ RSpec.describe Admin::ConfirmationsController, type: :controller do
   render_views
 
   before do
-    sign_in Fabricate(:user, admin: true), scope: :user
+    sign_in Fabricate(:user, role: UserRole.find_by(name: 'Admin')), scope: :user
   end
 
   describe 'POST #create' do
diff --git a/spec/controllers/admin/custom_emojis_controller_spec.rb b/spec/controllers/admin/custom_emojis_controller_spec.rb
index a8d96948c..06cd0c22d 100644
--- a/spec/controllers/admin/custom_emojis_controller_spec.rb
+++ b/spec/controllers/admin/custom_emojis_controller_spec.rb
@@ -3,7 +3,7 @@ require 'rails_helper'
 describe Admin::CustomEmojisController do
   render_views
 
-  let(:user) { Fabricate(:user, admin: true) }
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
 
   before do
     sign_in user, scope: :user
diff --git a/spec/controllers/admin/dashboard_controller_spec.rb b/spec/controllers/admin/dashboard_controller_spec.rb
index 7824854f9..6231a09a2 100644
--- a/spec/controllers/admin/dashboard_controller_spec.rb
+++ b/spec/controllers/admin/dashboard_controller_spec.rb
@@ -12,7 +12,7 @@ describe Admin::DashboardController, type: :controller do
         Admin::SystemCheck::Message.new(:rules_check, nil, admin_rules_path),
         Admin::SystemCheck::Message.new(:sidekiq_process_check, 'foo, bar'),
       ])
-      sign_in Fabricate(:user, admin: true)
+      sign_in Fabricate(:user, role: UserRole.find_by(name: 'Admin'))
     end
 
     it 'returns 200' do
diff --git a/spec/controllers/admin/disputes/appeals_controller_spec.rb b/spec/controllers/admin/disputes/appeals_controller_spec.rb
index 6a06f9406..712657791 100644
--- a/spec/controllers/admin/disputes/appeals_controller_spec.rb
+++ b/spec/controllers/admin/disputes/appeals_controller_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe Admin::Disputes::AppealsController, type: :controller do
   end
 
   describe 'POST #approve' do
-    let(:current_user) { Fabricate(:user, admin: true) }
+    let(:current_user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
 
     before do
       allow(UserMailer).to receive(:appeal_approved).and_return(double('email', deliver_later: nil))
@@ -35,7 +35,7 @@ RSpec.describe Admin::Disputes::AppealsController, type: :controller do
   end
 
   describe 'POST #reject' do
-    let(:current_user) { Fabricate(:user, admin: true) }
+    let(:current_user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
 
     before do
       allow(UserMailer).to receive(:appeal_rejected).and_return(double('email', deliver_later: nil))
diff --git a/spec/controllers/admin/domain_blocks_controller_spec.rb b/spec/controllers/admin/domain_blocks_controller_spec.rb
index ecc79292b..5c2dcd268 100644
--- a/spec/controllers/admin/domain_blocks_controller_spec.rb
+++ b/spec/controllers/admin/domain_blocks_controller_spec.rb
@@ -4,7 +4,7 @@ RSpec.describe Admin::DomainBlocksController, type: :controller do
   render_views
 
   before do
-    sign_in Fabricate(:user, admin: true), scope: :user
+    sign_in Fabricate(:user, role: UserRole.find_by(name: 'Admin')), scope: :user
   end
 
   describe 'GET #new' do
diff --git a/spec/controllers/admin/email_domain_blocks_controller_spec.rb b/spec/controllers/admin/email_domain_blocks_controller_spec.rb
index cf194579d..e9cef4a94 100644
--- a/spec/controllers/admin/email_domain_blocks_controller_spec.rb
+++ b/spec/controllers/admin/email_domain_blocks_controller_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe Admin::EmailDomainBlocksController, type: :controller do
   render_views
 
   before do
-    sign_in Fabricate(:user, admin: true), scope: :user
+    sign_in Fabricate(:user, role: UserRole.find_by(name: 'Admin')), scope: :user
   end
 
   describe 'GET #index' do
diff --git a/spec/controllers/admin/instances_controller_spec.rb b/spec/controllers/admin/instances_controller_spec.rb
index 53427b874..337f7a80c 100644
--- a/spec/controllers/admin/instances_controller_spec.rb
+++ b/spec/controllers/admin/instances_controller_spec.rb
@@ -3,7 +3,7 @@ require 'rails_helper'
 RSpec.describe Admin::InstancesController, type: :controller do
   render_views
 
-  let(:current_user) { Fabricate(:user, admin: true) }
+  let(:current_user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
 
   let!(:account)     { Fabricate(:account, domain: 'popular') }
   let!(:account2)    { Fabricate(:account, domain: 'popular') }
@@ -35,11 +35,11 @@ RSpec.describe Admin::InstancesController, type: :controller do
   describe 'DELETE #destroy' do
     subject { delete :destroy, params: { id: Instance.first.id } }
 
-    let(:current_user) { Fabricate(:user, admin: admin) }
+    let(:current_user) { Fabricate(:user, role: role) }
     let(:account) { Fabricate(:account) }
 
     context 'when user is admin' do
-      let(:admin) { true }
+      let(:role) { UserRole.find_by(name: 'Admin') }
 
       it 'succeeds in purging instance' do
         is_expected.to redirect_to admin_instances_path
@@ -47,7 +47,7 @@ RSpec.describe Admin::InstancesController, type: :controller do
     end
 
     context 'when user is not admin' do
-      let(:admin) { false }
+      let(:role) { nil }
 
       it 'fails to purge instance' do
         is_expected.to have_http_status :forbidden
diff --git a/spec/controllers/admin/invites_controller_spec.rb b/spec/controllers/admin/invites_controller_spec.rb
index 449a699e4..1fb488742 100644
--- a/spec/controllers/admin/invites_controller_spec.rb
+++ b/spec/controllers/admin/invites_controller_spec.rb
@@ -5,7 +5,7 @@ require 'rails_helper'
 describe Admin::InvitesController do
   render_views
 
-  let(:user) { Fabricate(:user, admin: true) }
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
 
   before do
     sign_in user, scope: :user
diff --git a/spec/controllers/admin/report_notes_controller_spec.rb b/spec/controllers/admin/report_notes_controller_spec.rb
index c0013f41a..fa7572d18 100644
--- a/spec/controllers/admin/report_notes_controller_spec.rb
+++ b/spec/controllers/admin/report_notes_controller_spec.rb
@@ -3,7 +3,7 @@ require 'rails_helper'
 describe Admin::ReportNotesController do
   render_views
 
-  let(:user) { Fabricate(:user, admin: true) }
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
 
   before do
     sign_in user, scope: :user
diff --git a/spec/controllers/admin/reports_controller_spec.rb b/spec/controllers/admin/reports_controller_spec.rb
index d421f0739..4cd1524bf 100644
--- a/spec/controllers/admin/reports_controller_spec.rb
+++ b/spec/controllers/admin/reports_controller_spec.rb
@@ -3,7 +3,7 @@ require 'rails_helper'
 describe Admin::ReportsController do
   render_views
 
-  let(:user) { Fabricate(:user, admin: true) }
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
   before do
     sign_in user, scope: :user
   end
diff --git a/spec/controllers/admin/resets_controller_spec.rb b/spec/controllers/admin/resets_controller_spec.rb
index 28510b5af..aeb172318 100644
--- a/spec/controllers/admin/resets_controller_spec.rb
+++ b/spec/controllers/admin/resets_controller_spec.rb
@@ -5,7 +5,7 @@ describe Admin::ResetsController do
 
   let(:account) { Fabricate(:account) }
   before do
-    sign_in Fabricate(:user, admin: true), scope: :user
+    sign_in Fabricate(:user, role: UserRole.find_by(name: 'Admin')), scope: :user
   end
 
   describe 'POST #create' do
diff --git a/spec/controllers/admin/roles_controller_spec.rb b/spec/controllers/admin/roles_controller_spec.rb
index 8e0de73cb..8ff891205 100644
--- a/spec/controllers/admin/roles_controller_spec.rb
+++ b/spec/controllers/admin/roles_controller_spec.rb
@@ -3,31 +3,247 @@ require 'rails_helper'
 describe Admin::RolesController do
   render_views
 
-  let(:admin) { Fabricate(:user, admin: true) }
+  let(:permissions)  { UserRole::Flags::NONE }
+  let(:current_role) { UserRole.create(name: 'Foo', permissions: permissions, position: 10) }
+  let(:current_user) { Fabricate(:user, role: current_role) }
 
   before do
-    sign_in admin, scope: :user
+    sign_in current_user, scope: :user
   end
 
-  describe 'POST #promote' do
-    subject { post :promote, params: { account_id: user.account_id } }
+  describe 'GET #index' do
+    before do
+      get :index
+    end
+
+    context 'when user does not have permission to manage roles' do
+      it 'returns http forbidden' do
+        expect(response).to have_http_status(:forbidden)
+      end
+    end
 
-    let(:user) { Fabricate(:user, moderator: false, admin: false) }
+    context 'when user has permission to manage roles' do
+      let(:permissions) { UserRole::FLAGS[:manage_roles] }
 
-    it 'promotes user' do
-      expect(subject).to redirect_to admin_account_path(user.account_id)
-      expect(user.reload).to be_moderator
+      it 'returns http success' do
+        expect(response).to have_http_status(:success)
+      end
     end
   end
 
-  describe 'POST #demote' do
-    subject { post :demote, params: { account_id: user.account_id } }
+  describe 'GET #new' do
+    before do
+      get :new
+    end
+
+    context 'when user does not have permission to manage roles' do
+      it 'returns http forbidden' do
+        expect(response).to have_http_status(:forbidden)
+      end
+    end
+
+    context 'when user has permission to manage roles' do
+      let(:permissions) { UserRole::FLAGS[:manage_roles] }
+
+      it 'returns http success' do
+        expect(response).to have_http_status(:success)
+      end
+    end
+  end
+
+  describe 'POST #create' do
+    let(:selected_position) { 1 }
+    let(:selected_permissions_as_keys) { %w(manage_roles) }
+
+    before do
+      post :create, params: { user_role: { name: 'Bar', position: selected_position, permissions_as_keys: selected_permissions_as_keys } }
+    end
+
+    context 'when user has permission to manage roles' do
+      let(:permissions) { UserRole::FLAGS[:manage_roles] }
+
+      context 'when new role\'s does not elevate above the user\'s role' do
+        let(:selected_position) { 1 }
+        let(:selected_permissions_as_keys) { %w(manage_roles) }
+
+        it 'redirects to roles page' do
+          expect(response).to redirect_to(admin_roles_path)
+        end
+
+        it 'creates new role' do
+          expect(UserRole.find_by(name: 'Bar')).to_not be_nil
+        end
+      end
+
+      context 'when new role\'s position is higher than user\'s role' do
+        let(:selected_position) { 100 }
+        let(:selected_permissions_as_keys) { %w(manage_roles) }
+
+        it 'renders new template' do
+          expect(response).to render_template(:new)
+        end
+
+        it 'does not create new role' do
+          expect(UserRole.find_by(name: 'Bar')).to be_nil
+        end
+      end
+
+      context 'when new role has permissions the user does not have' do
+        let(:selected_position) { 1 }
+        let(:selected_permissions_as_keys) { %w(manage_roles manage_users manage_reports) }
+
+        it 'renders new template' do
+          expect(response).to render_template(:new)
+        end
+
+        it 'does not create new role' do
+          expect(UserRole.find_by(name: 'Bar')).to be_nil
+        end
+      end
+
+      context 'when user has administrator permission' do
+        let(:permissions) { UserRole::FLAGS[:administrator] }
+
+        let(:selected_position) { 1 }
+        let(:selected_permissions_as_keys) { %w(manage_roles manage_users manage_reports) }
+
+        it 'redirects to roles page' do
+          expect(response).to redirect_to(admin_roles_path)
+        end
+
+        it 'creates new role' do
+          expect(UserRole.find_by(name: 'Bar')).to_not be_nil
+        end
+      end
+    end
+  end
+
+  describe 'GET #edit' do
+    let(:role_position) { 8 }
+    let(:role) { UserRole.create(name: 'Bar', permissions: UserRole::FLAGS[:manage_users], position: role_position) }
+
+    before do
+      get :edit, params: { id: role.id }
+    end
+
+    context 'when user does not have permission to manage roles' do
+      it 'returns http forbidden' do
+        expect(response).to have_http_status(:forbidden)
+      end
+    end
+
+    context 'when user has permission to manage roles' do
+      let(:permissions) { UserRole::FLAGS[:manage_roles] }
+
+      context 'when user outranks the role' do
+        it 'returns http success' do
+          expect(response).to have_http_status(:success)
+        end
+      end
+
+      context 'when role outranks user' do
+        let(:role_position) { current_role.position + 1 }
+
+        it 'returns http forbidden' do
+          expect(response).to have_http_status(:forbidden)
+        end
+      end
+    end
+  end
+
+  describe 'PUT #update' do
+    let(:role_position) { 8 }
+    let(:role_permissions) { UserRole::FLAGS[:manage_users] }
+    let(:role) { UserRole.create(name: 'Bar', permissions: role_permissions, position: role_position) }
+
+    let(:selected_position) { 8 }
+    let(:selected_permissions_as_keys) { %w(manage_users) }
+
+    before do
+      put :update, params: { id: role.id, user_role: { name: 'Baz', position: selected_position, permissions_as_keys: selected_permissions_as_keys } }
+    end
+
+    context 'when user does not have permission to manage roles' do
+      it 'returns http forbidden' do
+        expect(response).to have_http_status(:forbidden)
+      end
+
+      it 'does not update the role' do
+        expect(role.reload.name).to eq 'Bar'
+      end
+    end
+
+    context 'when user has permission to manage roles' do
+      let(:permissions) { UserRole::FLAGS[:manage_roles] }
+
+      context 'when role has permissions the user doesn\'t' do
+        it 'renders edit template' do
+          expect(response).to render_template(:edit)
+        end
+
+        it 'does not update the role' do
+          expect(role.reload.name).to eq 'Bar'
+        end
+      end
+
+      context 'when user has all permissions of the role' do
+        let(:permissions) { UserRole::FLAGS[:manage_roles] | UserRole::FLAGS[:manage_users] }
+
+        context 'when user outranks the role' do
+          it 'redirects to roles page' do
+            expect(response).to redirect_to(admin_roles_path)
+          end
+
+          it 'updates the role' do
+            expect(role.reload.name).to eq 'Baz'
+          end
+        end
+
+        context 'when role outranks user' do
+          let(:role_position) { current_role.position + 1 }
+
+          it 'returns http forbidden' do
+            expect(response).to have_http_status(:forbidden)
+          end
+
+          it 'does not update the role' do
+            expect(role.reload.name).to eq 'Bar'
+          end
+        end
+      end
+    end
+  end
+
+  describe 'DELETE #destroy' do
+    let(:role_position) { 8 }
+    let(:role) { UserRole.create(name: 'Bar', permissions: UserRole::FLAGS[:manage_users], position: role_position) }
+
+    before do
+      delete :destroy, params: { id: role.id }
+    end
+
+    context 'when user does not have permission to manage roles' do
+      it 'returns http forbidden' do
+        expect(response).to have_http_status(:forbidden)
+      end
+    end
+
+    context 'when user has permission to manage roles' do
+      let(:permissions) { UserRole::FLAGS[:manage_roles] }
+
+      context 'when user outranks the role' do
+        it 'redirects to roles page' do
+          expect(response).to redirect_to(admin_roles_path)
+        end
+      end
 
-    let(:user) { Fabricate(:user, moderator: true, admin: false) }
+      context 'when role outranks user' do
+        let(:role_position) { current_role.position + 1 }
 
-    it 'demotes user' do
-      expect(subject).to redirect_to admin_account_path(user.account_id)
-      expect(user.reload).not_to be_moderator
+        it 'returns http forbidden' do
+          expect(response).to have_http_status(:forbidden)
+        end
+      end
     end
   end
 end
diff --git a/spec/controllers/admin/settings_controller_spec.rb b/spec/controllers/admin/settings_controller_spec.rb
index 6cf0ee20a..46749f76c 100644
--- a/spec/controllers/admin/settings_controller_spec.rb
+++ b/spec/controllers/admin/settings_controller_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe Admin::SettingsController, type: :controller do
 
   describe 'When signed in as an admin' do
     before do
-      sign_in Fabricate(:user, admin: true), scope: :user
+      sign_in Fabricate(:user, role: UserRole.find_by(name: 'Admin')), scope: :user
     end
 
     describe 'GET #edit' do
diff --git a/spec/controllers/admin/statuses_controller_spec.rb b/spec/controllers/admin/statuses_controller_spec.rb
index de32fd18e..227688e23 100644
--- a/spec/controllers/admin/statuses_controller_spec.rb
+++ b/spec/controllers/admin/statuses_controller_spec.rb
@@ -3,7 +3,7 @@ require 'rails_helper'
 describe Admin::StatusesController do
   render_views
 
-  let(:user) { Fabricate(:user, admin: true) }
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
   let(:account) { Fabricate(:account) }
   let!(:status) { Fabricate(:status, account: account) }
   let(:media_attached_status) { Fabricate(:status, account: account, sensitive: !sensitive) }
diff --git a/spec/controllers/admin/tags_controller_spec.rb b/spec/controllers/admin/tags_controller_spec.rb
index 85c801a9c..52fd09eb1 100644
--- a/spec/controllers/admin/tags_controller_spec.rb
+++ b/spec/controllers/admin/tags_controller_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe Admin::TagsController, type: :controller do
   render_views
 
   before do
-    sign_in Fabricate(:user, admin: true)
+    sign_in Fabricate(:user, role: UserRole.find_by(name: 'Admin'))
   end
 
   describe 'GET #show' do
diff --git a/spec/controllers/admin/users/roles_controller.rb b/spec/controllers/admin/users/roles_controller.rb
new file mode 100644
index 000000000..bd6a3fa67
--- /dev/null
+++ b/spec/controllers/admin/users/roles_controller.rb
@@ -0,0 +1,81 @@
+require 'rails_helper'
+
+describe Admin::Users::RolesController do
+  render_views
+
+  let(:current_role) { UserRole.create(name: 'Foo', permissions: UserRole::FLAGS[:manage_roles], position: 10) }
+  let(:current_user) { Fabricate(:user, role: current_role) }
+
+  let(:previous_role) { nil }
+  let(:user) { Fabricate(:user, role: previous_role) }
+
+  before do
+    sign_in current_user, scope: :user
+  end
+
+  describe 'GET #show' do
+    before do
+      get :show, params: { user_id: user.id }
+    end
+
+    it 'returns http success' do
+      expect(response).to have_http_status(:success)
+    end
+
+    context 'when target user is higher ranked than current user' do
+      let(:previous_role) { UserRole.create(name: 'Baz', permissions: UserRole::FLAGS[:administrator], position: 100) }
+
+      it 'returns http forbidden' do
+        expect(response).to have_http_status(:forbidden)
+      end
+    end
+  end
+
+  describe 'PUT #update' do
+    let(:selected_role) { UserRole.create(name: 'Bar', permissions: permissions, position: position) }
+
+    before do
+      put :update, params: { user_id: user.id, user: { role_id: selected_role.id } }
+    end
+
+    context do
+      let(:permissions) { UserRole::FLAGS[:manage_roles] }
+      let(:position) { 1 }
+
+      it 'updates user role' do
+        expect(user.reload.role_id).to eq selected_role&.id
+      end
+
+      it 'redirects back to account page' do
+        expect(response).to redirect_to(admin_account_path(user.account_id))
+      end
+    end
+
+    context 'when selected role has higher position than current user\'s role' do
+      let(:permissions) { UserRole::FLAGS[:administrator] }
+      let(:position) { 100 }
+
+      it 'does not update user role' do
+        expect(user.reload.role_id).to eq previous_role&.id
+      end
+
+      it 'renders edit form' do
+        expect(response).to render_template(:show)
+      end
+    end
+
+    context 'when target user is higher ranked than current user' do
+      let(:previous_role) { UserRole.create(name: 'Baz', permissions: UserRole::FLAGS[:administrator], position: 100) }
+      let(:permissions) { UserRole::FLAGS[:manage_roles] }
+      let(:position) { 1 }
+
+      it 'does not update user role' do
+        expect(user.reload.role_id).to eq previous_role&.id
+      end
+
+      it 'returns http forbidden' do
+        expect(response).to have_http_status(:forbidden)
+      end
+    end
+  end
+end
diff --git a/spec/controllers/admin/two_factor_authentications_controller_spec.rb b/spec/controllers/admin/users/two_factor_authentications_controller_spec.rb
index c65095729..e56264ef6 100644
--- a/spec/controllers/admin/two_factor_authentications_controller_spec.rb
+++ b/spec/controllers/admin/users/two_factor_authentications_controller_spec.rb
@@ -1,12 +1,13 @@
 require 'rails_helper'
 require 'webauthn/fake_client'
 
-describe Admin::TwoFactorAuthenticationsController do
+describe Admin::Users::TwoFactorAuthenticationsController do
   render_views
 
   let(:user) { Fabricate(:user) }
+
   before do
-    sign_in Fabricate(:user, admin: true), scope: :user
+    sign_in Fabricate(:user, role: UserRole.find_by(name: 'Admin')), scope: :user
   end
 
   describe 'DELETE #destroy' do
diff --git a/spec/controllers/api/v1/admin/account_actions_controller_spec.rb b/spec/controllers/api/v1/admin/account_actions_controller_spec.rb
index 601290b82..199395f55 100644
--- a/spec/controllers/api/v1/admin/account_actions_controller_spec.rb
+++ b/spec/controllers/api/v1/admin/account_actions_controller_spec.rb
@@ -3,7 +3,7 @@ require 'rails_helper'
 RSpec.describe Api::V1::Admin::AccountActionsController, type: :controller do
   render_views
 
-  let(:role)   { 'moderator' }
+  let(:role)   { UserRole.find_by(name: 'Moderator') }
   let(:user)   { Fabricate(:user, role: role) }
   let(:scopes) { 'admin:read admin:write' }
   let(:token)  { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
@@ -22,7 +22,7 @@ RSpec.describe Api::V1::Admin::AccountActionsController, type: :controller do
   end
 
   shared_examples 'forbidden for wrong role' do |wrong_role|
-    let(:role) { wrong_role }
+    let(:role) { UserRole.find_by(name: wrong_role) }
 
     it 'returns http forbidden' do
       expect(response).to have_http_status(403)
@@ -35,7 +35,7 @@ RSpec.describe Api::V1::Admin::AccountActionsController, type: :controller do
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
-    it_behaves_like 'forbidden for wrong role', 'user'
+    it_behaves_like 'forbidden for wrong role', ''
 
     it 'returns http success' do
       expect(response).to have_http_status(200)
diff --git a/spec/controllers/api/v1/admin/accounts_controller_spec.rb b/spec/controllers/api/v1/admin/accounts_controller_spec.rb
index b69595f7e..cd38030e0 100644
--- a/spec/controllers/api/v1/admin/accounts_controller_spec.rb
+++ b/spec/controllers/api/v1/admin/accounts_controller_spec.rb
@@ -3,7 +3,7 @@ require 'rails_helper'
 RSpec.describe Api::V1::Admin::AccountsController, type: :controller do
   render_views
 
-  let(:role)   { 'moderator' }
+  let(:role)   { UserRole.find_by(name: 'Moderator') }
   let(:user)   { Fabricate(:user, role: role) }
   let(:scopes) { 'admin:read admin:write' }
   let(:token)  { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
@@ -22,7 +22,7 @@ RSpec.describe Api::V1::Admin::AccountsController, type: :controller do
   end
 
   shared_examples 'forbidden for wrong role' do |wrong_role|
-    let(:role) { wrong_role }
+    let(:role) { UserRole.find_by(name: wrong_role) }
 
     it 'returns http forbidden' do
       expect(response).to have_http_status(403)
@@ -46,7 +46,7 @@ RSpec.describe Api::V1::Admin::AccountsController, type: :controller do
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
-    it_behaves_like 'forbidden for wrong role', 'user'
+    it_behaves_like 'forbidden for wrong role', ''
 
     [
       [{ active: 'true', local: 'true', staff: 'true' }, [:admin_account]],
@@ -77,7 +77,7 @@ RSpec.describe Api::V1::Admin::AccountsController, type: :controller do
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
-    it_behaves_like 'forbidden for wrong role', 'user'
+    it_behaves_like 'forbidden for wrong role', ''
 
     it 'returns http success' do
       expect(response).to have_http_status(200)
@@ -91,7 +91,7 @@ RSpec.describe Api::V1::Admin::AccountsController, type: :controller do
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
-    it_behaves_like 'forbidden for wrong role', 'user'
+    it_behaves_like 'forbidden for wrong role', ''
 
     it 'returns http success' do
       expect(response).to have_http_status(200)
@@ -109,7 +109,7 @@ RSpec.describe Api::V1::Admin::AccountsController, type: :controller do
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
-    it_behaves_like 'forbidden for wrong role', 'user'
+    it_behaves_like 'forbidden for wrong role', ''
 
     it 'returns http success' do
       expect(response).to have_http_status(200)
@@ -127,7 +127,7 @@ RSpec.describe Api::V1::Admin::AccountsController, type: :controller do
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
-    it_behaves_like 'forbidden for wrong role', 'user'
+    it_behaves_like 'forbidden for wrong role', ''
 
     it 'returns http success' do
       expect(response).to have_http_status(200)
@@ -145,7 +145,7 @@ RSpec.describe Api::V1::Admin::AccountsController, type: :controller do
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
-    it_behaves_like 'forbidden for wrong role', 'user'
+    it_behaves_like 'forbidden for wrong role', ''
 
     it 'returns http success' do
       expect(response).to have_http_status(200)
@@ -163,7 +163,7 @@ RSpec.describe Api::V1::Admin::AccountsController, type: :controller do
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
-    it_behaves_like 'forbidden for wrong role', 'user'
+    it_behaves_like 'forbidden for wrong role', ''
 
     it 'returns http success' do
       expect(response).to have_http_status(200)
@@ -181,7 +181,7 @@ RSpec.describe Api::V1::Admin::AccountsController, type: :controller do
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
-    it_behaves_like 'forbidden for wrong role', 'user'
+    it_behaves_like 'forbidden for wrong role', ''
 
     it 'returns http success' do
       expect(response).to have_http_status(200)
diff --git a/spec/controllers/api/v1/admin/domain_allows_controller_spec.rb b/spec/controllers/api/v1/admin/domain_allows_controller_spec.rb
index edee3ab6c..26a391a60 100644
--- a/spec/controllers/api/v1/admin/domain_allows_controller_spec.rb
+++ b/spec/controllers/api/v1/admin/domain_allows_controller_spec.rb
@@ -3,7 +3,7 @@ require 'rails_helper'
 RSpec.describe Api::V1::Admin::DomainAllowsController, type: :controller do
   render_views
 
-  let(:role)   { 'admin' }
+  let(:role)   { UserRole.find_by(name: 'Admin') }
   let(:user)   { Fabricate(:user, role: role) }
   let(:scopes) { 'admin:read admin:write' }
   let(:token)  { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
@@ -21,7 +21,7 @@ RSpec.describe Api::V1::Admin::DomainAllowsController, type: :controller do
   end
 
   shared_examples 'forbidden for wrong role' do |wrong_role|
-    let(:role) { wrong_role }
+    let(:role) { UserRole.find_by(name: wrong_role) }
 
     it 'returns http forbidden' do
       expect(response).to have_http_status(403)
@@ -36,8 +36,8 @@ RSpec.describe Api::V1::Admin::DomainAllowsController, type: :controller do
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
-    it_behaves_like 'forbidden for wrong role', 'user'
-    it_behaves_like 'forbidden for wrong role', 'moderator'
+    it_behaves_like 'forbidden for wrong role', ''
+    it_behaves_like 'forbidden for wrong role', 'Moderator'
 
     it 'returns http success' do
       expect(response).to have_http_status(200)
@@ -58,8 +58,8 @@ RSpec.describe Api::V1::Admin::DomainAllowsController, type: :controller do
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
-    it_behaves_like 'forbidden for wrong role', 'user'
-    it_behaves_like 'forbidden for wrong role', 'moderator'
+    it_behaves_like 'forbidden for wrong role', ''
+    it_behaves_like 'forbidden for wrong role', 'Moderator'
 
     it 'returns http success' do
       expect(response).to have_http_status(200)
@@ -79,8 +79,8 @@ RSpec.describe Api::V1::Admin::DomainAllowsController, type: :controller do
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
-    it_behaves_like 'forbidden for wrong role', 'user'
-    it_behaves_like 'forbidden for wrong role', 'moderator'
+    it_behaves_like 'forbidden for wrong role', ''
+    it_behaves_like 'forbidden for wrong role', 'Moderator'
 
     it 'returns http success' do
       expect(response).to have_http_status(200)
@@ -99,8 +99,8 @@ RSpec.describe Api::V1::Admin::DomainAllowsController, type: :controller do
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
-    it_behaves_like 'forbidden for wrong role', 'user'
-    it_behaves_like 'forbidden for wrong role', 'moderator'
+    it_behaves_like 'forbidden for wrong role', ''
+    it_behaves_like 'forbidden for wrong role', 'Moderator'
 
     it 'returns http success' do
       expect(response).to have_http_status(200)
diff --git a/spec/controllers/api/v1/admin/domain_blocks_controller_spec.rb b/spec/controllers/api/v1/admin/domain_blocks_controller_spec.rb
index 196f6dc28..f12285b2a 100644
--- a/spec/controllers/api/v1/admin/domain_blocks_controller_spec.rb
+++ b/spec/controllers/api/v1/admin/domain_blocks_controller_spec.rb
@@ -3,7 +3,7 @@ require 'rails_helper'
 RSpec.describe Api::V1::Admin::DomainBlocksController, type: :controller do
   render_views
 
-  let(:role)   { 'admin' }
+  let(:role)   { UserRole.find_by(name: 'Admin') }
   let(:user)   { Fabricate(:user, role: role) }
   let(:scopes) { 'admin:read admin:write' }
   let(:token)  { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
@@ -21,7 +21,7 @@ RSpec.describe Api::V1::Admin::DomainBlocksController, type: :controller do
   end
 
   shared_examples 'forbidden for wrong role' do |wrong_role|
-    let(:role) { wrong_role }
+    let(:role) { UserRole.find_by(name: wrong_role) }
 
     it 'returns http forbidden' do
       expect(response).to have_http_status(403)
@@ -36,8 +36,8 @@ RSpec.describe Api::V1::Admin::DomainBlocksController, type: :controller do
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
-    it_behaves_like 'forbidden for wrong role', 'user'
-    it_behaves_like 'forbidden for wrong role', 'moderator'
+    it_behaves_like 'forbidden for wrong role', ''
+    it_behaves_like 'forbidden for wrong role', 'Moderator'
 
     it 'returns http success' do
       expect(response).to have_http_status(200)
@@ -58,8 +58,8 @@ RSpec.describe Api::V1::Admin::DomainBlocksController, type: :controller do
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
-    it_behaves_like 'forbidden for wrong role', 'user'
-    it_behaves_like 'forbidden for wrong role', 'moderator'
+    it_behaves_like 'forbidden for wrong role', ''
+    it_behaves_like 'forbidden for wrong role', 'Moderator'
 
     it 'returns http success' do
       expect(response).to have_http_status(200)
@@ -79,8 +79,8 @@ RSpec.describe Api::V1::Admin::DomainBlocksController, type: :controller do
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
-    it_behaves_like 'forbidden for wrong role', 'user'
-    it_behaves_like 'forbidden for wrong role', 'moderator'
+    it_behaves_like 'forbidden for wrong role', ''
+    it_behaves_like 'forbidden for wrong role', 'Moderator'
 
     it 'returns http success' do
       expect(response).to have_http_status(200)
@@ -100,8 +100,8 @@ RSpec.describe Api::V1::Admin::DomainBlocksController, type: :controller do
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
-    it_behaves_like 'forbidden for wrong role', 'user'
-    it_behaves_like 'forbidden for wrong role', 'moderator'
+    it_behaves_like 'forbidden for wrong role', ''
+    it_behaves_like 'forbidden for wrong role', 'Moderator'
 
     it 'returns http success' do
       expect(response).to have_http_status(200)
diff --git a/spec/controllers/api/v1/admin/reports_controller_spec.rb b/spec/controllers/api/v1/admin/reports_controller_spec.rb
index b6df53048..880e72030 100644
--- a/spec/controllers/api/v1/admin/reports_controller_spec.rb
+++ b/spec/controllers/api/v1/admin/reports_controller_spec.rb
@@ -3,7 +3,7 @@ require 'rails_helper'
 RSpec.describe Api::V1::Admin::ReportsController, type: :controller do
   render_views
 
-  let(:role)   { 'moderator' }
+  let(:role)   { UserRole.find_by(name: 'Moderator') }
   let(:user)   { Fabricate(:user, role: role) }
   let(:scopes) { 'admin:read admin:write' }
   let(:token)  { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
@@ -22,7 +22,7 @@ RSpec.describe Api::V1::Admin::ReportsController, type: :controller do
   end
 
   shared_examples 'forbidden for wrong role' do |wrong_role|
-    let(:role) { wrong_role }
+    let(:role) { UserRole.find_by(name: wrong_role) }
 
     it 'returns http forbidden' do
       expect(response).to have_http_status(403)
@@ -35,7 +35,7 @@ RSpec.describe Api::V1::Admin::ReportsController, type: :controller do
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
-    it_behaves_like 'forbidden for wrong role', 'user'
+    it_behaves_like 'forbidden for wrong role', ''
 
     it 'returns http success' do
       expect(response).to have_http_status(200)
@@ -48,7 +48,7 @@ RSpec.describe Api::V1::Admin::ReportsController, type: :controller do
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
-    it_behaves_like 'forbidden for wrong role', 'user'
+    it_behaves_like 'forbidden for wrong role', ''
 
     it 'returns http success' do
       expect(response).to have_http_status(200)
@@ -61,7 +61,7 @@ RSpec.describe Api::V1::Admin::ReportsController, type: :controller do
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
-    it_behaves_like 'forbidden for wrong role', 'user'
+    it_behaves_like 'forbidden for wrong role', ''
 
     it 'returns http success' do
       expect(response).to have_http_status(200)
@@ -74,7 +74,7 @@ RSpec.describe Api::V1::Admin::ReportsController, type: :controller do
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
-    it_behaves_like 'forbidden for wrong role', 'user'
+    it_behaves_like 'forbidden for wrong role', ''
 
     it 'returns http success' do
       expect(response).to have_http_status(200)
@@ -87,7 +87,7 @@ RSpec.describe Api::V1::Admin::ReportsController, type: :controller do
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
-    it_behaves_like 'forbidden for wrong role', 'user'
+    it_behaves_like 'forbidden for wrong role', ''
 
     it 'returns http success' do
       expect(response).to have_http_status(200)
@@ -100,7 +100,7 @@ RSpec.describe Api::V1::Admin::ReportsController, type: :controller do
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
-    it_behaves_like 'forbidden for wrong role', 'user'
+    it_behaves_like 'forbidden for wrong role', ''
 
     it 'returns http success' do
       expect(response).to have_http_status(200)
diff --git a/spec/controllers/api/v1/reports_controller_spec.rb b/spec/controllers/api/v1/reports_controller_spec.rb
index b5baf60e1..dbc64e704 100644
--- a/spec/controllers/api/v1/reports_controller_spec.rb
+++ b/spec/controllers/api/v1/reports_controller_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe Api::V1::ReportsController, type: :controller do
   end
 
   describe 'POST #create' do
-    let!(:admin) { Fabricate(:user, admin: true) }
+    let!(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
 
     let(:scopes) { 'write:reports' }
     let(:status) { Fabricate(:status) }
diff --git a/spec/controllers/api/v2/admin/accounts_controller_spec.rb b/spec/controllers/api/v2/admin/accounts_controller_spec.rb
index 3212ddb84..2508a9e05 100644
--- a/spec/controllers/api/v2/admin/accounts_controller_spec.rb
+++ b/spec/controllers/api/v2/admin/accounts_controller_spec.rb
@@ -3,7 +3,7 @@ require 'rails_helper'
 RSpec.describe Api::V2::Admin::AccountsController, type: :controller do
   render_views
 
-  let(:role)   { 'moderator' }
+  let(:role)   { UserRole.find_by(name: 'Moderator') }
   let(:user)   { Fabricate(:user, role: role) }
   let(:scopes) { 'admin:read admin:write' }
   let(:token)  { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
@@ -22,7 +22,7 @@ RSpec.describe Api::V2::Admin::AccountsController, type: :controller do
   end
 
   shared_examples 'forbidden for wrong role' do |wrong_role|
-    let(:role) { wrong_role }
+    let(:role) { UserRole.find_by(name: wrong_role) }
 
     it 'returns http forbidden' do
       expect(response).to have_http_status(403)
@@ -46,7 +46,7 @@ RSpec.describe Api::V2::Admin::AccountsController, type: :controller do
     end
 
     it_behaves_like 'forbidden for wrong scope', 'write:statuses'
-    it_behaves_like 'forbidden for wrong role', 'user'
+    it_behaves_like 'forbidden for wrong role', ''
 
     [
       [{ status: 'active', origin: 'local', permissions: 'staff' }, [:admin_account]],
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index 53e163d49..1b002e01c 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -183,70 +183,6 @@ describe ApplicationController, type: :controller do
     end
   end
 
-  describe 'require_admin!' do
-    controller do
-      before_action :require_admin!
-
-      def success
-        head 200
-      end
-    end
-
-    before do
-      routes.draw { get 'success' => 'anonymous#success' }
-    end
-
-    it 'returns a 403 if current user is not admin' do
-      sign_in(Fabricate(:user, admin: false))
-      get 'success'
-      expect(response).to have_http_status(403)
-    end
-
-    it 'returns a 403 if current user is only a moderator' do
-      sign_in(Fabricate(:user, moderator: true))
-      get 'success'
-      expect(response).to have_http_status(403)
-    end
-
-    it 'does nothing if current user is admin' do
-      sign_in(Fabricate(:user, admin: true))
-      get 'success'
-      expect(response).to have_http_status(200)
-    end
-  end
-
-  describe 'require_staff!' do
-    controller do
-      before_action :require_staff!
-
-      def success
-        head 200
-      end
-    end
-
-    before do
-      routes.draw { get 'success' => 'anonymous#success' }
-    end
-
-    it 'returns a 403 if current user is not admin or moderator' do
-      sign_in(Fabricate(:user, admin: false, moderator: false))
-      get 'success'
-      expect(response).to have_http_status(403)
-    end
-
-    it 'does nothing if current user is moderator' do
-      sign_in(Fabricate(:user, moderator: true))
-      get 'success'
-      expect(response).to have_http_status(200)
-    end
-
-    it 'does nothing if current user is admin' do
-      sign_in(Fabricate(:user, admin: true))
-      get 'success'
-      expect(response).to have_http_status(200)
-    end
-  end
-
   describe 'forbidden' do
     controller do
       def route_forbidden
diff --git a/spec/controllers/disputes/appeals_controller_spec.rb b/spec/controllers/disputes/appeals_controller_spec.rb
index faa571fc9..90f222f49 100644
--- a/spec/controllers/disputes/appeals_controller_spec.rb
+++ b/spec/controllers/disputes/appeals_controller_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe Disputes::AppealsController, type: :controller do
 
   before { sign_in current_user, scope: :user }
 
-  let!(:admin) { Fabricate(:user, admin: true) }
+  let!(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
 
   describe '#create' do
     let(:current_user) { Fabricate(:user) }
diff --git a/spec/controllers/invites_controller_spec.rb b/spec/controllers/invites_controller_spec.rb
index 76e617e6b..23b98fb12 100644
--- a/spec/controllers/invites_controller_spec.rb
+++ b/spec/controllers/invites_controller_spec.rb
@@ -7,30 +7,30 @@ describe InvitesController do
     sign_in user
   end
 
-  around do |example|
-    min_invite_role = Setting.min_invite_role
-    example.run
-    Setting.min_invite_role = min_invite_role
-  end
-
   describe 'GET #index' do
     subject { get :index }
 
-    let(:user) { Fabricate(:user, moderator: false, admin: false) }
+    let(:user) { Fabricate(:user) }
     let!(:invite) { Fabricate(:invite, user: user) }
 
-    context 'when user is a staff' do
+    context 'when everyone can invite' do
+      before do
+        UserRole.everyone.update(permissions: UserRole.everyone.permissions | UserRole::FLAGS[:invite_users])
+      end
+
       it 'renders index page' do
-        Setting.min_invite_role = 'user'
         expect(subject).to render_template :index
         expect(assigns(:invites)).to include invite
         expect(assigns(:invites).count).to eq 1
       end
     end
 
-    context 'when user is not a staff' do
+    context 'when not everyone can invite' do
+      before do
+        UserRole.everyone.update(permissions: UserRole.everyone.permissions & ~UserRole::FLAGS[:invite_users])
+      end
+
       it 'returns 403' do
-        Setting.min_invite_role = 'modelator'
         expect(subject).to have_http_status 403
       end
     end
@@ -39,8 +39,12 @@ describe InvitesController do
   describe 'POST #create' do
     subject { post :create, params: { invite: { max_uses: '10', expires_in: 1800 } } }
 
-    context 'when user is an admin' do
-      let(:user) { Fabricate(:user, moderator: false, admin: true) }
+    context 'when everyone can invite' do
+      let(:user) { Fabricate(:user) }
+
+      before do
+        UserRole.everyone.update(permissions: UserRole.everyone.permissions | UserRole::FLAGS[:invite_users])
+      end
 
       it 'succeeds to create a invite' do
         expect { subject }.to change { Invite.count }.by(1)
@@ -49,8 +53,12 @@ describe InvitesController do
       end
     end
 
-    context 'when user is not an admin' do
-      let(:user) { Fabricate(:user, moderator: true, admin: false) }
+    context 'when not everyone can invite' do
+      let(:user) { Fabricate(:user) }
+
+      before do
+        UserRole.everyone.update(permissions: UserRole.everyone.permissions & ~UserRole::FLAGS[:invite_users])
+      end
 
       it 'returns 403' do
         expect(subject).to have_http_status 403
@@ -61,8 +69,8 @@ describe InvitesController do
   describe 'DELETE #create' do
     subject { delete :destroy, params: { id: invite.id } }
 
+    let(:user) { Fabricate(:user) }
     let!(:invite) { Fabricate(:invite, user: user, expires_at: nil) }
-    let(:user) { Fabricate(:user, moderator: false, admin: true) }
 
     it 'expires invite' do
       expect(subject).to redirect_to invites_path
diff --git a/spec/fabricators/user_role_fabricator.rb b/spec/fabricators/user_role_fabricator.rb
new file mode 100644
index 000000000..28f76c8c4
--- /dev/null
+++ b/spec/fabricators/user_role_fabricator.rb
@@ -0,0 +1,5 @@
+Fabricator(:user_role) do
+  name        "MyString"
+  color       "MyString"
+  permissions ""
+end
\ No newline at end of file
diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb
index dc0ca3da3..467d41836 100644
--- a/spec/models/account_spec.rb
+++ b/spec/models/account_spec.rb
@@ -445,7 +445,7 @@ RSpec.describe Account, type: :model do
 
     it 'accepts arbitrary limits' do
       2.times.each { Fabricate(:account, display_name: "Display Name") }
-      results = Account.search_for("display", 1)
+      results = Account.search_for("display", limit: 1)
       expect(results.size).to eq 1
     end
 
@@ -473,7 +473,7 @@ RSpec.describe Account, type: :model do
         )
         account.follow!(match)
 
-        results = Account.advanced_search_for('A?l\i:c e', account, 10, true)
+        results = Account.advanced_search_for('A?l\i:c e', account, limit: 10, following: true)
         expect(results).to eq [match]
       end
 
@@ -485,7 +485,7 @@ RSpec.describe Account, type: :model do
           domain: 'example.com'
         )
 
-        results = Account.advanced_search_for('A?l\i:c e', account, 10, true)
+        results = Account.advanced_search_for('A?l\i:c e', account, limit: 10, following: true)
         expect(results).to eq []
       end
 
@@ -498,7 +498,7 @@ RSpec.describe Account, type: :model do
           suspended: true
         )
 
-        results = Account.advanced_search_for('username', account, 10, true)
+        results = Account.advanced_search_for('username', account, limit: 10, following: true)
         expect(results).to eq []
       end
 
@@ -511,7 +511,7 @@ RSpec.describe Account, type: :model do
 
         match.user.update(approved: false)
 
-        results = Account.advanced_search_for('username', account, 10, true)
+        results = Account.advanced_search_for('username', account, limit: 10, following: true)
         expect(results).to eq []
       end
 
@@ -524,7 +524,7 @@ RSpec.describe Account, type: :model do
 
         match.user.update(confirmed_at: nil)
 
-        results = Account.advanced_search_for('username', account, 10, true)
+        results = Account.advanced_search_for('username', account, limit: 10, following: true)
         expect(results).to eq []
       end
     end
@@ -588,7 +588,7 @@ RSpec.describe Account, type: :model do
 
     it 'accepts arbitrary limits' do
       2.times { Fabricate(:account, display_name: "Display Name") }
-      results = Account.advanced_search_for("display", account, 1)
+      results = Account.advanced_search_for("display", account, limit: 1)
       expect(results.size).to eq 1
     end
 
diff --git a/spec/models/admin/account_action_spec.rb b/spec/models/admin/account_action_spec.rb
index 809c7fc46..b6a052b76 100644
--- a/spec/models/admin/account_action_spec.rb
+++ b/spec/models/admin/account_action_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe Admin::AccountAction, type: :model do
 
   describe '#save!' do
     subject              { account_action.save! }
-    let(:account)        { Fabricate(:user, admin: true).account }
+    let(:account)        { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
     let(:target_account) { Fabricate(:account) }
     let(:type)           { 'disable' }
 
diff --git a/spec/models/user_role_spec.rb b/spec/models/user_role_spec.rb
new file mode 100644
index 000000000..28019593e
--- /dev/null
+++ b/spec/models/user_role_spec.rb
@@ -0,0 +1,189 @@
+require 'rails_helper'
+
+RSpec.describe UserRole, type: :model do
+  subject { described_class.create(name: 'Foo', position: 1) }
+
+  describe '#can?' do
+    context 'with a single flag' do
+      it 'returns true if any of them are present' do
+        subject.permissions = UserRole::FLAGS[:manage_reports]
+        expect(subject.can?(:manage_reports)).to be true
+      end
+
+      it 'returns false if it is not set' do
+        expect(subject.can?(:manage_reports)).to be false
+      end
+    end
+
+    context 'with multiple flags' do
+      it 'returns true if any of them are present' do
+        subject.permissions = UserRole::FLAGS[:manage_users]
+        expect(subject.can?(:manage_reports, :manage_users)).to be true
+      end
+
+      it 'returns false if none of them are present' do
+        expect(subject.can?(:manage_reports, :manage_users)).to be false
+      end
+    end
+
+    context 'with an unknown flag' do
+      it 'raises an error' do
+        expect { subject.can?(:foo) }.to raise_error ArgumentError
+      end
+    end
+  end
+
+  describe '#overrides?' do
+    it 'returns true if other role has lower position' do
+      expect(subject.overrides?(described_class.new(position: subject.position - 1))).to be true
+    end
+
+    it 'returns true if other role is nil' do
+      expect(subject.overrides?(nil)).to be true
+    end
+
+    it 'returns false if other role has higher position' do
+      expect(subject.overrides?(described_class.new(position: subject.position + 1))).to be false
+    end
+  end
+
+  describe '#permissions_as_keys' do
+    before do
+      subject.permissions = UserRole::FLAGS[:invite_users] | UserRole::FLAGS[:view_dashboard] | UserRole::FLAGS[:manage_reports]
+    end
+
+    it 'returns an array' do
+      expect(subject.permissions_as_keys).to match_array %w(invite_users view_dashboard manage_reports)
+    end
+  end
+
+  describe '#permissions_as_keys=' do
+    let(:input) { }
+
+    before do
+      subject.permissions_as_keys = input
+    end
+
+    context 'with a single value' do
+      let(:input) { %w(manage_users) }
+
+      it 'sets permission flags' do
+        expect(subject.permissions).to eq UserRole::FLAGS[:manage_users]
+      end
+    end
+
+    context 'with multiple values' do
+      let(:input) { %w(manage_users manage_reports) }
+
+      it 'sets permission flags' do
+        expect(subject.permissions).to eq UserRole::FLAGS[:manage_users] | UserRole::FLAGS[:manage_reports]
+      end
+    end
+
+    context 'with an unknown value' do
+      let(:input) { %w(foo) }
+
+      it 'does not set permission flags' do
+        expect(subject.permissions).to eq UserRole::Flags::NONE
+      end
+    end
+  end
+
+  describe '#computed_permissions' do
+    context 'when the role is nobody' do
+      let(:subject) { described_class.nobody }
+
+      it 'returns none' do
+        expect(subject.computed_permissions).to eq UserRole::Flags::NONE
+      end
+    end
+
+    context 'when the role is everyone' do
+      let(:subject) { described_class.everyone }
+
+      it 'returns permissions' do
+        expect(subject.computed_permissions).to eq subject.permissions
+      end
+    end
+
+    context 'when role has the administrator flag' do
+      before do
+        subject.permissions = UserRole::FLAGS[:administrator]
+      end
+
+      it 'returns all permissions' do
+        expect(subject.computed_permissions).to eq UserRole::Flags::ALL
+      end
+    end
+
+    context do
+      it 'returns permissions combined with the everyone role' do
+        expect(subject.computed_permissions).to eq described_class.everyone.permissions
+      end
+    end
+  end
+
+  describe '.everyone' do
+    subject { described_class.everyone }
+
+    it 'returns a role' do
+      expect(subject).to be_kind_of(described_class)
+    end
+
+    it 'is identified as the everyone role' do
+      expect(subject.everyone?).to be true
+    end
+
+    it 'has default permissions' do
+      expect(subject.permissions).to eq UserRole::FLAGS[:invite_users]
+    end
+
+    it 'has negative position' do
+      expect(subject.position).to eq -1
+    end
+  end
+
+  describe '.nobody' do
+    subject { described_class.nobody }
+
+    it 'returns a role' do
+      expect(subject).to be_kind_of(described_class)
+    end
+
+    it 'is identified as the nobody role' do
+      expect(subject.nobody?).to be true
+    end
+
+    it 'has no permissions' do
+      expect(subject.permissions).to eq UserRole::Flags::NONE
+    end
+
+    it 'has negative position' do
+      expect(subject.position).to eq -1
+    end
+  end
+
+  describe '#everyone?' do
+    it 'returns true when id is -99' do
+      subject.id = -99
+      expect(subject.everyone?).to be true
+    end
+
+    it 'returns false when id is not -99' do
+      subject.id = 123
+      expect(subject.everyone?).to be false
+    end
+  end
+
+  describe '#nobody?' do
+    it 'returns true when id is nil' do
+      subject.id = nil
+      expect(subject.nobody?).to be true
+    end
+
+    it 'returns false when id is not nil' do
+      subject.id = 123
+      expect(subject.nobody?).to be false
+    end
+  end
+end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 1645ab59e..a7da31e60 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -56,14 +56,6 @@ RSpec.describe User, type: :model do
       end
     end
 
-    describe 'admins' do
-      it 'returns an array of users who are admin' do
-        user_1 = Fabricate(:user, admin: false)
-        user_2 = Fabricate(:user, admin: true)
-        expect(User.admins).to match_array([user_2])
-      end
-    end
-
     describe 'confirmed' do
       it 'returns an array of users who are confirmed' do
         user_1 = Fabricate(:user, confirmed_at: nil)
@@ -289,49 +281,6 @@ RSpec.describe User, type: :model do
     end
   end
 
-  describe '#role' do
-    it 'returns admin for admin' do
-      user = User.new(admin: true)
-      expect(user.role).to eq 'admin'
-    end
-
-    it 'returns moderator for moderator' do
-      user = User.new(moderator: true)
-      expect(user.role).to eq 'moderator'
-    end
-
-    it 'returns user otherwise' do
-      user = User.new
-      expect(user.role).to eq 'user'
-    end
-  end
-
-  describe '#role?' do
-    it 'returns false when invalid role requested' do
-      user = User.new(admin: true)
-      expect(user.role?('disabled')).to be false
-    end
-
-    it 'returns true when exact role match' do
-      user  = User.new
-      mod   = User.new(moderator: true)
-      admin = User.new(admin: true)
-
-      expect(user.role?('user')).to be true
-      expect(mod.role?('moderator')).to be true
-      expect(admin.role?('admin')).to be true
-    end
-
-    it 'returns true when role higher than needed' do
-      mod   = User.new(moderator: true)
-      admin = User.new(admin: true)
-
-      expect(mod.role?('user')).to be true
-      expect(admin.role?('user')).to be true
-      expect(admin.role?('moderator')).to be true
-    end
-  end
-
   describe '#disable!' do
     subject(:user) { Fabricate(:user, disabled: false, current_sign_in_at: current_sign_in_at, last_sign_in_at: nil) }
     let(:current_sign_in_at) { Time.zone.now }
@@ -420,110 +369,6 @@ RSpec.describe User, type: :model do
     end
   end
 
-  describe '#promote!' do
-    subject(:user) { Fabricate(:user, admin: is_admin, moderator: is_moderator) }
-
-    before do
-      user.promote!
-    end
-
-    context 'when user is an admin' do
-      let(:is_admin) { true }
-
-      context 'when user is a moderator' do
-        let(:is_moderator) { true }
-
-        it 'changes moderator filed false' do
-          expect(user).to be_admin
-          expect(user).not_to be_moderator
-        end
-      end
-
-      context 'when user is not a moderator' do
-        let(:is_moderator) { false }
-
-        it 'does not change status' do
-          expect(user).to be_admin
-          expect(user).not_to be_moderator
-        end
-      end
-    end
-
-    context 'when user is not admin' do
-      let(:is_admin) { false }
-
-      context 'when user is a moderator' do
-        let(:is_moderator) { true }
-
-        it 'changes user into an admin' do
-          expect(user).to be_admin
-          expect(user).not_to be_moderator
-        end
-      end
-
-      context 'when user is not a moderator' do
-        let(:is_moderator) { false }
-
-        it 'changes user into a moderator' do
-          expect(user).not_to be_admin
-          expect(user).to be_moderator
-        end
-      end
-    end
-  end
-
-  describe '#demote!' do
-    subject(:user) { Fabricate(:user, admin: admin, moderator: moderator) }
-
-    before do
-      user.demote!
-    end
-
-    context 'when user is an admin' do
-      let(:admin) { true }
-
-      context 'when user is a moderator' do
-        let(:moderator) { true }
-
-        it 'changes user into a moderator' do
-          expect(user).not_to be_admin
-          expect(user).to be_moderator
-        end
-      end
-
-      context 'when user is not a moderator' do
-        let(:moderator) { false }
-
-        it 'changes user into a moderator' do
-          expect(user).not_to be_admin
-          expect(user).to be_moderator
-        end
-      end
-    end
-
-    context 'when user is not an admin' do
-      let(:admin) { false }
-
-      context 'when user is a moderator' do
-        let(:moderator) { true }
-
-        it 'changes user into a plain user' do
-          expect(user).not_to be_admin
-          expect(user).not_to be_moderator
-        end
-      end
-
-      context 'when user is not a moderator' do
-        let(:moderator) { false }
-
-        it 'does not change any fields' do
-          expect(user).not_to be_admin
-          expect(user).not_to be_moderator
-        end
-      end
-    end
-  end
-
   describe '#active_for_authentication?' do
     subject { user.active_for_authentication? }
     let(:user) { Fabricate(:user, disabled: disabled, confirmed_at: confirmed_at) }
@@ -560,4 +405,8 @@ RSpec.describe User, type: :model do
       end
     end
   end
+
+  describe '.those_who_can' do
+    pending
+  end
 end
diff --git a/spec/policies/account_moderation_note_policy_spec.rb b/spec/policies/account_moderation_note_policy_spec.rb
index 39ec2008a..846747346 100644
--- a/spec/policies/account_moderation_note_policy_spec.rb
+++ b/spec/policies/account_moderation_note_policy_spec.rb
@@ -5,7 +5,7 @@ require 'pundit/rspec'
 
 RSpec.describe AccountModerationNotePolicy do
   let(:subject) { described_class }
-  let(:admin)   { Fabricate(:user, admin: true).account }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
   let(:john)    { Fabricate(:account) }
 
   permissions :create? do
@@ -31,7 +31,7 @@ RSpec.describe AccountModerationNotePolicy do
 
     context 'admin' do
       it 'grants to destroy' do
-        expect(subject).to permit(admin, AccountModerationNotePolicy)
+        expect(subject).to permit(admin, account_moderation_note)
       end
     end
 
diff --git a/spec/policies/account_policy_spec.rb b/spec/policies/account_policy_spec.rb
index b55eb65a7..0f23fd97e 100644
--- a/spec/policies/account_policy_spec.rb
+++ b/spec/policies/account_policy_spec.rb
@@ -5,7 +5,7 @@ require 'pundit/rspec'
 
 RSpec.describe AccountPolicy do
   let(:subject) { described_class }
-  let(:admin)   { Fabricate(:user, admin: true).account }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
   let(:john)    { Fabricate(:account) }
   let(:alice)   { Fabricate(:account) }
 
@@ -55,7 +55,7 @@ RSpec.describe AccountPolicy do
     end
   end
 
-  permissions :redownload?, :subscribe?, :unsubscribe? do
+  permissions :redownload? do
     context 'admin' do
       it 'permits' do
         expect(subject).to permit(admin)
@@ -70,7 +70,7 @@ RSpec.describe AccountPolicy do
   end
 
   permissions :suspend?, :silence? do
-    let(:staff) { Fabricate(:user, admin: true).account }
+    let(:staff) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
 
     context 'staff' do
       context 'record is staff' do
@@ -94,7 +94,7 @@ RSpec.describe AccountPolicy do
   end
 
   permissions :memorialize? do
-    let(:other_admin) { Fabricate(:user, admin: true).account }
+    let(:other_admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
 
     context 'admin' do
       context 'record is admin' do
diff --git a/spec/policies/custom_emoji_policy_spec.rb b/spec/policies/custom_emoji_policy_spec.rb
index e4f1af3c1..6a6ef6694 100644
--- a/spec/policies/custom_emoji_policy_spec.rb
+++ b/spec/policies/custom_emoji_policy_spec.rb
@@ -5,7 +5,7 @@ require 'pundit/rspec'
 
 RSpec.describe CustomEmojiPolicy do
   let(:subject) { described_class }
-  let(:admin)   { Fabricate(:user, admin: true).account }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
   let(:john)    { Fabricate(:account) }
 
   permissions :index?, :enable?, :disable? do
diff --git a/spec/policies/domain_block_policy_spec.rb b/spec/policies/domain_block_policy_spec.rb
index b24ed9e3a..01b97e823 100644
--- a/spec/policies/domain_block_policy_spec.rb
+++ b/spec/policies/domain_block_policy_spec.rb
@@ -5,7 +5,7 @@ require 'pundit/rspec'
 
 RSpec.describe DomainBlockPolicy do
   let(:subject) { described_class }
-  let(:admin)   { Fabricate(:user, admin: true).account }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
   let(:john)    { Fabricate(:account) }
 
   permissions :index?, :show?, :create?, :destroy? do
diff --git a/spec/policies/email_domain_block_policy_spec.rb b/spec/policies/email_domain_block_policy_spec.rb
index 1ff55af8e..913075c3d 100644
--- a/spec/policies/email_domain_block_policy_spec.rb
+++ b/spec/policies/email_domain_block_policy_spec.rb
@@ -5,7 +5,7 @@ require 'pundit/rspec'
 
 RSpec.describe EmailDomainBlockPolicy do
   let(:subject) { described_class }
-  let(:admin)   { Fabricate(:user, admin: true).account }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
   let(:john)    { Fabricate(:account) }
 
   permissions :index?, :create?, :destroy? do
diff --git a/spec/policies/instance_policy_spec.rb b/spec/policies/instance_policy_spec.rb
index 71ef1fe50..f6f51af06 100644
--- a/spec/policies/instance_policy_spec.rb
+++ b/spec/policies/instance_policy_spec.rb
@@ -5,7 +5,7 @@ require 'pundit/rspec'
 
 RSpec.describe InstancePolicy do
   let(:subject) { described_class }
-  let(:admin)   { Fabricate(:user, admin: true).account }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
   let(:john)    { Fabricate(:account) }
 
   permissions :index?, :show?, :destroy? do
diff --git a/spec/policies/invite_policy_spec.rb b/spec/policies/invite_policy_spec.rb
index 122137804..01660322f 100644
--- a/spec/policies/invite_policy_spec.rb
+++ b/spec/policies/invite_policy_spec.rb
@@ -5,8 +5,8 @@ require 'pundit/rspec'
 
 RSpec.describe InvitePolicy do
   let(:subject) { described_class }
-  let(:admin)   { Fabricate(:user, admin: true).account }
-  let(:john)    { Fabricate(:account) }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
+  let(:john)    { Fabricate(:user).account }
 
   permissions :index? do
     context 'staff?' do
@@ -17,16 +17,22 @@ RSpec.describe InvitePolicy do
   end
 
   permissions :create? do
-    context 'min_required_role?' do
+    context 'has privilege' do
+      before do
+        UserRole.everyone.update(permissions: UserRole::FLAGS[:invite_users])
+      end
+
       it 'permits' do
-        allow_any_instance_of(described_class).to receive(:min_required_role?) { true }
         expect(subject).to permit(john, Invite)
       end
     end
 
-    context 'not min_required_role?' do
+    context 'does not have privilege' do
+      before do
+        UserRole.everyone.update(permissions: UserRole::Flags::NONE)
+      end
+
       it 'denies' do
-        allow_any_instance_of(described_class).to receive(:min_required_role?) { false }
         expect(subject).to_not permit(john, Invite)
       end
     end
@@ -54,39 +60,15 @@ RSpec.describe InvitePolicy do
     end
 
     context 'not owner?' do
-      context 'Setting.min_invite_role == "admin"' do
-        before do
-          Setting.min_invite_role = 'admin'
-        end
-
-        context 'admin?' do
-          it 'permits' do
-            expect(subject).to permit(admin, Fabricate(:invite))
-          end
-        end
-
-        context 'not admin?' do
-          it 'denies' do
-            expect(subject).to_not permit(john, Fabricate(:invite))
-          end
+      context 'admin?' do
+        it 'permits' do
+          expect(subject).to permit(admin, Fabricate(:invite))
         end
       end
 
-      context 'Setting.min_invite_role != "admin"' do
-        before do
-          Setting.min_invite_role = 'else'
-        end
-
-        context 'staff?' do
-          it 'permits' do
-            expect(subject).to permit(admin, Fabricate(:invite))
-          end
-        end
-
-        context 'not staff?' do
-          it 'denies' do
-            expect(subject).to_not permit(john, Fabricate(:invite))
-          end
+      context 'not admin?' do
+        it 'denies' do
+          expect(subject).to_not permit(john, Fabricate(:invite))
         end
       end
     end
diff --git a/spec/policies/relay_policy_spec.rb b/spec/policies/relay_policy_spec.rb
index 139d945dc..2c50ba1e9 100644
--- a/spec/policies/relay_policy_spec.rb
+++ b/spec/policies/relay_policy_spec.rb
@@ -5,7 +5,7 @@ require 'pundit/rspec'
 
 RSpec.describe RelayPolicy do
   let(:subject) { described_class }
-  let(:admin)   { Fabricate(:user, admin: true).account }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
   let(:john)    { Fabricate(:account) }
 
   permissions :update? do
diff --git a/spec/policies/report_note_policy_spec.rb b/spec/policies/report_note_policy_spec.rb
index c34f99b71..99f5ffb8e 100644
--- a/spec/policies/report_note_policy_spec.rb
+++ b/spec/policies/report_note_policy_spec.rb
@@ -5,7 +5,7 @@ require 'pundit/rspec'
 
 RSpec.describe ReportNotePolicy do
   let(:subject) { described_class }
-  let(:admin)   { Fabricate(:user, admin: true).account }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
   let(:john)    { Fabricate(:account) }
 
   permissions :create? do
@@ -25,7 +25,8 @@ RSpec.describe ReportNotePolicy do
   permissions :destroy? do
     context 'admin?' do
       it 'permit' do
-        expect(subject).to permit(admin, ReportNote)
+        report_note = Fabricate(:report_note, account: john)
+        expect(subject).to permit(admin, report_note)
       end
     end
 
diff --git a/spec/policies/report_policy_spec.rb b/spec/policies/report_policy_spec.rb
index 84c366d7f..8b005d8dd 100644
--- a/spec/policies/report_policy_spec.rb
+++ b/spec/policies/report_policy_spec.rb
@@ -5,7 +5,7 @@ require 'pundit/rspec'
 
 RSpec.describe ReportPolicy do
   let(:subject) { described_class }
-  let(:admin)   { Fabricate(:user, admin: true).account }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
   let(:john)    { Fabricate(:account) }
 
   permissions :update?, :index?, :show? do
diff --git a/spec/policies/settings_policy_spec.rb b/spec/policies/settings_policy_spec.rb
index 3fa183c50..e16ee51a4 100644
--- a/spec/policies/settings_policy_spec.rb
+++ b/spec/policies/settings_policy_spec.rb
@@ -5,7 +5,7 @@ require 'pundit/rspec'
 
 RSpec.describe SettingsPolicy do
   let(:subject) { described_class }
-  let(:admin)   { Fabricate(:user, admin: true).account }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
   let(:john)    { Fabricate(:account) }
 
   permissions :update?, :show? do
diff --git a/spec/policies/status_policy_spec.rb b/spec/policies/status_policy_spec.rb
index 28b808ee2..205ecd720 100644
--- a/spec/policies/status_policy_spec.rb
+++ b/spec/policies/status_policy_spec.rb
@@ -6,7 +6,7 @@ require 'pundit/rspec'
 RSpec.describe StatusPolicy, type: :model do
   subject { described_class }
 
-  let(:admin) { Fabricate(:user, admin: true) }
+  let(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
   let(:alice) { Fabricate(:account, username: 'alice') }
   let(:bob) { Fabricate(:account, username: 'bob') }
   let(:status) { Fabricate(:status, account: alice) }
diff --git a/spec/policies/tag_policy_spec.rb b/spec/policies/tag_policy_spec.rb
index 256e6786a..9be7140fc 100644
--- a/spec/policies/tag_policy_spec.rb
+++ b/spec/policies/tag_policy_spec.rb
@@ -5,7 +5,7 @@ require 'pundit/rspec'
 
 RSpec.describe TagPolicy do
   let(:subject) { described_class }
-  let(:admin)   { Fabricate(:user, admin: true).account }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
   let(:john)    { Fabricate(:account) }
 
   permissions :index?, :show?, :update? do
diff --git a/spec/policies/user_policy_spec.rb b/spec/policies/user_policy_spec.rb
index 731c041d1..ff0916674 100644
--- a/spec/policies/user_policy_spec.rb
+++ b/spec/policies/user_policy_spec.rb
@@ -5,7 +5,7 @@ require 'pundit/rspec'
 
 RSpec.describe UserPolicy do
   let(:subject) { described_class }
-  let(:admin)   { Fabricate(:user, admin: true).account }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
   let(:john)    { Fabricate(:account) }
 
   permissions :reset_password?, :change_email? do
@@ -111,57 +111,4 @@ RSpec.describe UserPolicy do
       end
     end
   end
-
-  permissions :promote? do
-    context 'admin?' do
-      context 'promotable?' do
-        it 'permits' do
-          expect(subject).to permit(admin, john.user)
-        end
-      end
-
-      context '!promotable?' do
-        it 'denies' do
-          expect(subject).to_not permit(admin, admin.user)
-        end
-      end
-    end
-
-    context '!admin?' do
-      it 'denies' do
-        expect(subject).to_not permit(john, User)
-      end
-    end
-  end
-
-  permissions :demote? do
-    context 'admin?' do
-      context '!record.admin?' do
-        context 'demoteable?' do
-          it 'permits' do
-            john.user.update(moderator: true)
-            expect(subject).to permit(admin, john.user)
-          end
-        end
-
-        context '!demoteable?' do
-          it 'denies' do
-            expect(subject).to_not permit(admin, john.user)
-          end
-        end
-      end
-
-      context 'record.admin?' do
-        it 'denies' do
-          expect(subject).to_not permit(admin, admin.user)
-        end
-      end
-    end
-
-    context '!admin?' do
-      it 'denies' do
-        expect(subject).to_not permit(john, User)
-      end
-    end
-  end
 end