Skip to content
Snippets Groups Projects
Commit 3d849771 authored by Ilja's avatar Ilja Committed by Haelwenn
Browse files

Add privileges to Moderator dropdown

A dropdown in Users and Statuses views.
parent 9fa1902f
No related branches found
No related tags found
3 merge requests!326Mergeback: 2.5.0,!315Handle moderation privileges,!282Release/2.5.0
<template>
<el-dropdown :hide-on-click="false" size="small" trigger="click" placement="top-start" @click.native.stop>
<el-dropdown v-if="isPrivileged(['users_manage_activation_state', 'users_delete', 'users_manage_tags', 'users_manage_credentials'], ['admin'])" :hide-on-click="false" size="small" trigger="click" placement="top-start" @click.native.stop>
<div>
<el-button v-if="page === 'users'" type="text" class="el-dropdown-link">
{{ $t('users.moderation') }}
......@@ -17,6 +17,7 @@
</div>
<el-dropdown-menu slot="dropdown" class="moderation-dropdown-menu">
<el-dropdown-item
v-if="isPrivileged([], ['admin'])"
class="actor-type-dropdown">
<el-select v-model="actorType" :placeholder="$t('userProfile.actorType')" class="actor-type-select">
<el-option :label="$t('users.service')" value="Service"/>
......@@ -24,51 +25,51 @@
</el-select>
</el-dropdown-item>
<el-dropdown-item
v-if="showAdminAction(user)"
v-if="isPrivileged([], ['admin']) && showAdminAction(user)"
divided
@click.native="toggleUserRight(user, 'admin')">
{{ user.roles.admin ? $t('users.revokeAdmin') : $t('users.grantAdmin') }}
</el-dropdown-item>
<el-dropdown-item
v-if="showAdminAction(user)"
v-if="isPrivileged([], ['admin']) && showAdminAction(user)"
@click.native="toggleUserRight(user, 'moderator')">
{{ user.roles.moderator ? $t('users.revokeModerator') : $t('users.grantModerator') }}
</el-dropdown-item>
<el-dropdown-item
v-if="showDeactivatedButton(user.id) && page !== 'statusPage'"
v-if="isPrivileged(['users_manage_activation_state'], []) && showDeactivatedButton(user.id) && page !== 'statusPage'"
:divided="showAdminAction(user)"
@click.native="toggleActivation(user)">
{{ !user.is_active ? $t('users.activateAccount') : $t('users.deactivateAccount') }}
</el-dropdown-item>
<el-dropdown-item
v-if="showDeactivatedButton(user.id) && page !== 'statusPage'"
v-if="isPrivileged(['users_delete'], []) && showDeactivatedButton(user.id) && page !== 'statusPage'"
@click.native="handleDeletion(user)">
{{ $t('users.deleteAccount') }}
</el-dropdown-item>
<el-dropdown-item
v-if="user.local && !user.is_approved"
v-if="isPrivileged([], ['admin']) && user.local && !user.is_approved"
divided
@click.native="handleAccountApproval(user)">
{{ $t('users.approveAccount') }}
</el-dropdown-item>
<el-dropdown-item
v-if="user.local && !user.is_approved"
v-if="isPrivileged([], ['admin']) && user.local && !user.is_approved"
@click.native="handleAccountRejection(user)">
{{ $t('users.rejectAccount') }}
</el-dropdown-item>
<el-dropdown-item
v-if="user.local && !user.is_confirmed"
v-if="isPrivileged([], ['admin']) && user.local && !user.is_confirmed"
divided
@click.native="handleEmailConfirmation(user)">
{{ $t('users.confirmAccount') }}
</el-dropdown-item>
<el-dropdown-item
v-if="user.local && !user.is_confirmed"
v-if="isPrivileged([], ['admin']) && user.local && !user.is_confirmed"
@click.native="handleConfirmationResend(user)">
{{ $t('users.resendConfirmation') }}
</el-dropdown-item>
<el-dropdown-item
v-if="tagPolicyEnabled"
v-if="isPrivileged(['users_manage_tags'], []) && tagPolicyEnabled"
:divided="showAdminAction(user)"
:class="{ 'active-tag': user.tags.includes('mrf_tag:media-force-nsfw') }"
@click.native="toggleTag(user, 'mrf_tag:media-force-nsfw')">
......@@ -76,60 +77,60 @@
<i v-if="user.tags.includes('mrf_tag:media-force-nsfw')" class="el-icon-check"/>
</el-dropdown-item>
<el-dropdown-item
v-if="tagPolicyEnabled"
v-if="isPrivileged(['users_manage_tags'], []) && tagPolicyEnabled"
:class="{ 'active-tag': user.tags.includes('mrf_tag:media-strip') }"
@click.native="toggleTag(user, 'mrf_tag:media-strip')">
{{ $t('users.stripMedia') }}
<i v-if="user.tags.includes('mrf_tag:media-strip')" class="el-icon-check"/>
</el-dropdown-item>
<el-dropdown-item
v-if="tagPolicyEnabled"
v-if="isPrivileged(['users_manage_tags'], []) && tagPolicyEnabled"
:class="{ 'active-tag': user.tags.includes('mrf_tag:force-unlisted') }"
@click.native="toggleTag(user, 'mrf_tag:force-unlisted')">
{{ $t('users.forceUnlisted') }}
<i v-if="user.tags.includes('mrf_tag:force-unlisted')" class="el-icon-check"/>
</el-dropdown-item>
<el-dropdown-item
v-if="tagPolicyEnabled"
v-if="isPrivileged(['users_manage_tags'], []) && tagPolicyEnabled"
:class="{ 'active-tag': user.tags.includes('mrf_tag:sandbox') }"
@click.native="toggleTag(user, 'mrf_tag:sandbox')">
{{ $t('users.sandbox') }}
<i v-if="user.tags.includes('mrf_tag:sandbox')" class="el-icon-check"/>
</el-dropdown-item>
<el-dropdown-item
v-if="user.local && tagPolicyEnabled"
v-if="isPrivileged(['users_manage_tags'], []) && user.local && tagPolicyEnabled"
:class="{ 'active-tag': user.tags.includes('mrf_tag:disable-remote-subscription') }"
@click.native="toggleTag(user, 'mrf_tag:disable-remote-subscription')">
{{ $t('users.disableRemoteSubscription') }}
<i v-if="user.tags.includes('mrf_tag:disable-remote-subscription')" class="el-icon-check"/>
</el-dropdown-item>
<el-dropdown-item
v-if="user.local && tagPolicyEnabled"
v-if="isPrivileged(['users_manage_tags'], []) && user.local && tagPolicyEnabled"
:class="{ 'active-tag': user.tags.includes('mrf_tag:disable-any-subscription') }"
@click.native="toggleTag(user, 'mrf_tag:disable-any-subscription')">
{{ $t('users.disableAnySubscription') }}
<i v-if="user.tags.includes('mrf_tag:disable-any-subscription')" class="el-icon-check"/>
</el-dropdown-item>
<el-dropdown-item
v-if="!tagPolicyEnabled"
v-if="isPrivileged(['users_manage_tags'], []) && isPrivileged([], ['admin']) && !tagPolicyEnabled"
divided
class="no-hover"
@click.native="enableTagPolicy">
{{ $t('users.enableTagPolicy') }}
</el-dropdown-item>
<el-dropdown-item
v-if="user.local"
v-if="isPrivileged(['users_manage_credentials'], []) && user.local"
divided
@click.native="getPasswordResetToken(user.nickname)">
{{ $t('users.getPasswordResetToken') }}
</el-dropdown-item>
<el-dropdown-item
v-if="user.local"
v-if="isPrivileged([], ['admin']) && user.local"
@click.native="requirePasswordReset(user)">
{{ $t('users.requirePasswordReset') }}
</el-dropdown-item>
<el-dropdown-item
v-if="user.local"
v-if="isPrivileged([], ['admin']) && user.local"
@click.native="disableMfa(user.nickname)">
{{ $t('users.disableMfa') }}
</el-dropdown-item>
......@@ -181,6 +182,11 @@ export default {
disableMfa(nickname) {
this.$store.dispatch('DisableMfa', nickname)
},
isPrivileged(accepted_privileges, accepted_roles) {
const user_privileges = this.$store.getters.privileges
const user_roles = this.$store.getters.roles
return accepted_privileges.some(privilege => user_privileges.indexOf(privilege) >= 0) || accepted_roles.some(role => user_roles.indexOf(role) >= 0)
},
enableTagPolicy() {
this.$confirm(
this.$t('users.confirmEnablingTagPolicy'),
......
......@@ -13,7 +13,14 @@ export default {
peers,
settings,
status,
user,
user: {
...user,
state: {
...user.state,
roles: ['admin'],
privileges: ['users_manage_activation_state', 'users_delete', 'users_manage_tags', 'users_manage_credentials']
}
},
userProfile,
users
},
......
import Vuex from 'vuex'
import { mount, createLocalVue, config, RouterLinkStub } from '@vue/test-utils'
import flushPromises from 'flush-promises'
import Element from 'element-ui'
import Users from '@/views/users/index'
import {
storeNoPrivilegesNoRoles,
storeWithTagPolicyNoPrivilegesRolesAdmin,
storeWithPrivilegesUsersManageActivationStateNoRoles,
storeWithPrivilegesUsersDeleteNoRoles,
storeWithTagPolicyPrivilegesUsersManageTagsNoRoles,
storeWithTagPolicyPrivilegesUsersManageTagsRolesAdmin,
storeWithPrivilegesUsersManageCredentialsNoRoles
} from './store.conf'
import { cloneDeep } from 'lodash'
config.mocks["$t"] = (key) => key
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(Element)
jest.mock('@/api/app')
jest.mock('@/api/nodeInfo')
jest.mock('@/api/users')
jest.mock('@/api/settings')
describe('The Multiple Users Moderation Menu', () => {
it('doesnt show for someone with no privileges and no roles', async (done) => {
const store = new Vuex.Store(cloneDeep(storeNoPrivilegesNoRoles))
const wrapper = mount(Users, {
store,
localVue,
sync: false,
stubs: {
RouterLink: RouterLinkStub
}
})
await flushPromises()
expect(wrapper.find('.moderation-dropdown-menu').exists()).toEqual(false)
done()
})
it('shows for someone with admin role and shows proper entries', async (done) => {
const store = new Vuex.Store(cloneDeep(storeWithTagPolicyNoPrivilegesRolesAdmin))
const wrapper = mount(Users, {
store,
localVue,
sync: false,
stubs: {
RouterLink: RouterLinkStub
}
})
await flushPromises()
const menu = wrapper.findAll('.moderation-dropdown-menu').at(0)
const menu_items = menu.findAll('.el-dropdown-menu__item')
const menu_items_text = menu_items.wrappers.map(menu_item => menu_item.text()).sort()
expect(menu.isVisible()).toEqual(true)
expect(menu_items_text).toEqual([
'users.disableMfa',
'users.grantModerator',
'users.requirePasswordReset',
'users.revokeAdmin',
'users.service users.person',
])
store.state.users.mrfPolicies = []
await flushPromises()
expect(menu_items.length).toEqual(menu_items_text.length)
done()
})
it('shows for someone with users_manage_activation_state privilege and shows proper entries', async (done) => {
const store = new Vuex.Store(cloneDeep(storeWithPrivilegesUsersManageActivationStateNoRoles))
const wrapper = mount(Users, {
store,
localVue,
sync: false,
stubs: {
RouterLink: RouterLinkStub
}
})
await flushPromises()
const menu = wrapper.findAll('.moderation-dropdown-menu').at(0)
const menu_items = menu.findAll('.el-dropdown-menu__item')
const menu_items_text = menu_items.wrappers.map(menu_item => menu_item.text()).sort()
expect(menu.isVisible()).toEqual(true)
expect(menu_items_text).toEqual(['users.deactivateAccount'])
done()
})
it('shows for someone with users_delete privilege and shows proper entries', async (done) => {
const store = new Vuex.Store(cloneDeep(storeWithPrivilegesUsersDeleteNoRoles))
const wrapper = mount(Users, {
store,
localVue,
sync: false,
stubs: {
RouterLink: RouterLinkStub
}
})
await flushPromises()
const menu = wrapper.findAll('.moderation-dropdown-menu').at(0)
const menu_items = menu.findAll('.el-dropdown-menu__item')
const menu_items_text = menu_items.wrappers.map(menu_item => menu_item.text()).sort()
expect(menu.isVisible()).toEqual(true)
expect(menu_items_text).toEqual(['users.deleteAccount'])
done()
})
it('shows for someone with users_manage_tags privilege and shows proper entries depending on wether tagpolicy is set', async (done) => {
const store = new Vuex.Store(cloneDeep(storeWithTagPolicyPrivilegesUsersManageTagsNoRoles))
const wrapper = mount(Users, {
store,
localVue,
sync: false,
stubs: {
RouterLink: RouterLinkStub
}
})
await flushPromises()
const menu = wrapper.findAll('.moderation-dropdown-menu').at(0)
const menu_items = menu.findAll('.el-dropdown-menu__item')
const menu_items_text = menu_items.wrappers.map(menu_item => menu_item.text()).sort()
expect(menu.isVisible()).toEqual(true)
expect(menu_items_text).toEqual([
'users.disableAnySubscription',
'users.disableRemoteSubscription',
'users.forceNsfw',
'users.forceUnlisted',
'users.sandbox',
'users.stripMedia'
])
store.state.users.mrfPolicies = []
await flushPromises()
expect(menu.findAll('.el-dropdown-menu__item').length).toEqual(0)
done()
})
it('shows enable tagpolicy for someone with users_manage_tags privilege and admin role when tagpolicy is not set', async (done) => {
const store = new Vuex.Store(cloneDeep(storeWithTagPolicyPrivilegesUsersManageTagsRolesAdmin))
const wrapper = mount(Users, {
store,
localVue,
sync: false,
stubs: {
RouterLink: RouterLinkStub
}
})
await flushPromises()
const menu = wrapper.findAll('.moderation-dropdown-menu').at(0)
const menu_items = menu.findAll('.el-dropdown-menu__item')
const menu_items_text = menu_items.wrappers.map(menu_item => menu_item.text())
expect(menu.isVisible()).toEqual(true)
expect(menu_items_text.includes('users.disableAnySubscription')).toBe(true)
expect(menu_items_text.includes('users.disableRemoteSubscription')).toBe(true)
expect(menu_items_text.includes('users.forceNsfw')).toBe(true)
expect(menu_items_text.includes('users.forceUnlisted')).toBe(true)
expect(menu_items_text.includes('users.sandbox')).toBe(true)
expect(menu_items_text.includes('users.stripMedia')).toBe(true)
store.state.users.mrfPolicies = []
await flushPromises()
const menu_items_text_no_policy = menu.findAll('.el-dropdown-menu__item').wrappers.map(menu_item => menu_item.text())
expect(menu_items_text_no_policy.includes('users.disableAnySubscription')).toBe(false)
expect(menu_items_text_no_policy.includes('users.disableRemoteSubscription')).toBe(false)
expect(menu_items_text_no_policy.includes('users.forceNsfw')).toBe(false)
expect(menu_items_text_no_policy.includes('users.forceUnlisted')).toBe(false)
expect(menu_items_text_no_policy.includes('users.sandbox')).toBe(false)
expect(menu_items_text_no_policy.includes('users.stripMedia')).toBe(false)
expect(menu_items_text_no_policy.includes('users.enableTagPolicy')).toBe(true)
done()
})
it('shows for someone with users_manage_credentials privilege and shows proper entries', async (done) => {
const store = new Vuex.Store(cloneDeep(storeWithPrivilegesUsersManageCredentialsNoRoles))
const wrapper = mount(Users, {
store,
localVue,
sync: false,
stubs: {
RouterLink: RouterLinkStub
}
})
await flushPromises()
const menu = wrapper.findAll('.moderation-dropdown-menu').at(0)
const menu_items = menu.findAll('.el-dropdown-menu__item')
const menu_items_text = menu_items.wrappers.map(menu_item => menu_item.text()).sort()
expect(menu.isVisible()).toEqual(true)
expect(menu_items_text).toEqual(['users.getPasswordResetToken'])
done()
})
})
......@@ -12,6 +12,7 @@ export const storeNoPrivilegesNoRoles = {
user: {
...user,
state: {
...user.state,
roles: [],
privileges: []
}
......@@ -29,6 +30,7 @@ export const storeWithTagPolicyNoPrivilegesRolesAdmin = {
user: {
...user,
state: {
...user.state,
roles: ['admin'],
privileges: []
}
......@@ -46,6 +48,7 @@ export const storeWithPrivilegesUsersManageInvitesNoRoles = {
user: {
...user,
state: {
...user.state,
roles: [],
privileges: ['users_manage_invites']
}
......@@ -63,6 +66,7 @@ export const storeWithPrivilegesUsersDeleteNoRoles = {
user: {
...user,
state: {
...user.state,
roles: [],
privileges: ['users_delete']
}
......@@ -80,6 +84,7 @@ export const storeWithPrivilegesUsersManageActivationStateNoRoles = {
user: {
...user,
state: {
...user.state,
roles: [],
privileges: ['users_manage_activation_state']
}
......@@ -97,6 +102,7 @@ export const storeWithTagPolicyPrivilegesUsersManageTagsNoRoles = {
user: {
...user,
state: {
...user.state,
roles: [],
privileges: ['users_manage_tags']
}
......@@ -114,6 +120,7 @@ export const storeWithTagPolicyPrivilegesUsersManageTagsRolesAdmin = {
user: {
...user,
state: {
...user.state,
roles: ['admin'],
privileges: ['users_manage_tags']
}
......@@ -123,3 +130,21 @@ export const storeWithTagPolicyPrivilegesUsersManageTagsRolesAdmin = {
},
getters
}
export const storeWithPrivilegesUsersManageCredentialsNoRoles = {
modules: {
app,
settings,
user: {
...user,
state: {
...user.state,
roles: [],
privileges: ['users_manage_credentials']
}
},
userProfile,
users
},
getters
}
......@@ -9,7 +9,14 @@ export const storeConfig = {
modules: {
app,
settings,
user,
user: {
...user,
state: {
...user.state,
roles: ['admin'],
privileges: ['users_manage_activation_state', 'users_delete', 'users_manage_tags', 'users_manage_credentials']
}
},
userProfile,
users
},
......@@ -20,9 +27,22 @@ export const storeWithTagPolicy = {
modules: {
app,
settings,
user,
user: {
...user,
state: {
...user.state,
roles: ['admin'],
privileges: ['users_manage_activation_state', 'users_delete', 'users_manage_tags', 'users_manage_credentials']
}
},
userProfile,
users: { ...users, state: { ...users.state, mrfPolicies: ['Pleroma.Web.ActivityPub.MRF.TagPolicy'] }}
users: {
...users,
state: {
...users.state,
mrfPolicies: ['Pleroma.Web.ActivityPub.MRF.TagPolicy']
}
}
},
getters
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment