feat: page Rules access check
This commit is contained in:
@@ -7,40 +7,42 @@
|
||||
v-list-tile.pt-2(to='/dashboard')
|
||||
v-list-tile-avatar: v-icon dashboard
|
||||
v-list-tile-title {{ $t('admin:dashboard.title') }}
|
||||
v-divider.my-2
|
||||
v-subheader.pl-4 {{ $t('admin:nav.site') }}
|
||||
v-list-tile(to='/general', v-if='hasPermission(`manage:system`)')
|
||||
v-list-tile-avatar: v-icon widgets
|
||||
v-list-tile-title {{ $t('admin:general.title') }}
|
||||
v-list-tile(to='/locale', v-if='hasPermission(`manage:system`)')
|
||||
v-list-tile-avatar: v-icon language
|
||||
v-list-tile-title {{ $t('admin:locale.title') }}
|
||||
v-list-tile(to='/navigation', v-if='hasPermission([`manage:system`, `manage:navigation`])')
|
||||
v-list-tile-avatar: v-icon near_me
|
||||
v-list-tile-title {{ $t('admin:navigation.title') }}
|
||||
v-list-tile(to='/pages')
|
||||
v-list-tile-avatar: v-icon insert_drive_file
|
||||
v-list-tile-title {{ $t('admin:pages.title') }}
|
||||
v-list-tile-action
|
||||
v-chip(small, disabled, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`')
|
||||
.caption.grey--text {{ info.pagesTotal }}
|
||||
v-list-tile(to='/theme', v-if='hasPermission([`manage:system`, `manage:theme`])')
|
||||
v-list-tile-avatar: v-icon palette
|
||||
v-list-tile-title {{ $t('admin:theme.title') }}
|
||||
v-divider.my-2
|
||||
v-subheader.pl-4 {{ $t('admin:nav.users') }}
|
||||
v-list-tile(to='/groups')
|
||||
v-list-tile-avatar: v-icon people
|
||||
v-list-tile-title {{ $t('admin:groups.title') }}
|
||||
v-list-tile-action
|
||||
v-chip(small, disabled, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`')
|
||||
.caption.grey--text {{ info.groupsTotal }}
|
||||
v-list-tile(to='/users')
|
||||
v-list-tile-avatar: v-icon perm_identity
|
||||
v-list-tile-title {{ $t('admin:users.title') }}
|
||||
v-list-tile-action
|
||||
v-chip(small, disabled, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`')
|
||||
.caption.grey--text {{ info.usersTotal }}
|
||||
template(v-if='hasPermission([`manage:system`, `manage:navigation`, `write:pages`, `manage:pages`, `delete:pages`])')
|
||||
v-divider.my-2
|
||||
v-subheader.pl-4 {{ $t('admin:nav.site') }}
|
||||
v-list-tile(to='/general', v-if='hasPermission(`manage:system`)')
|
||||
v-list-tile-avatar: v-icon widgets
|
||||
v-list-tile-title {{ $t('admin:general.title') }}
|
||||
v-list-tile(to='/locale', v-if='hasPermission(`manage:system`)')
|
||||
v-list-tile-avatar: v-icon language
|
||||
v-list-tile-title {{ $t('admin:locale.title') }}
|
||||
v-list-tile(to='/navigation', v-if='hasPermission([`manage:system`, `manage:navigation`])')
|
||||
v-list-tile-avatar: v-icon near_me
|
||||
v-list-tile-title {{ $t('admin:navigation.title') }}
|
||||
v-list-tile(to='/pages', v-if='hasPermission([`manage:system`, `write:pages`, `manage:pages`, `delete:pages`])')
|
||||
v-list-tile-avatar: v-icon insert_drive_file
|
||||
v-list-tile-title {{ $t('admin:pages.title') }}
|
||||
v-list-tile-action
|
||||
v-chip(small, disabled, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`')
|
||||
.caption.grey--text {{ info.pagesTotal }}
|
||||
v-list-tile(to='/theme', v-if='hasPermission([`manage:system`, `manage:theme`])')
|
||||
v-list-tile-avatar: v-icon palette
|
||||
v-list-tile-title {{ $t('admin:theme.title') }}
|
||||
template(v-if='hasPermission([`manage:system`, `manage:groups`, `write:groups`, `manage:users`, `write:users`])')
|
||||
v-divider.my-2
|
||||
v-subheader.pl-4 {{ $t('admin:nav.users') }}
|
||||
v-list-tile(to='/groups', v-if='hasPermission([`manage:system`, `manage:groups`, `write:groups`])')
|
||||
v-list-tile-avatar: v-icon people
|
||||
v-list-tile-title {{ $t('admin:groups.title') }}
|
||||
v-list-tile-action
|
||||
v-chip(small, disabled, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`')
|
||||
.caption.grey--text {{ info.groupsTotal }}
|
||||
v-list-tile(to='/users', v-if='hasPermission([`manage:system`, `manage:groups`, `write:groups`, `manage:users`, `write:users`])')
|
||||
v-list-tile-avatar: v-icon perm_identity
|
||||
v-list-tile-title {{ $t('admin:users.title') }}
|
||||
v-list-tile-action
|
||||
v-chip(small, disabled, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`')
|
||||
.caption.grey--text {{ info.usersTotal }}
|
||||
template(v-if='hasPermission(`manage:system`)')
|
||||
v-divider.my-2
|
||||
v-subheader.pl-4 {{ $t('admin:nav.modules') }}
|
||||
@@ -62,8 +64,8 @@
|
||||
v-list-tile(to='/storage')
|
||||
v-list-tile-avatar: v-icon storage
|
||||
v-list-tile-title {{ $t('admin:storage.title') }}
|
||||
v-divider.my-2
|
||||
template(v-if='hasPermission([`manage:system`, `manage:api`])')
|
||||
v-divider.my-2
|
||||
v-subheader.pl-4 {{ $t('admin:nav.system') }}
|
||||
v-list-tile(to='/api', v-if='hasPermission([`manage:system`, `manage:api`])')
|
||||
v-list-tile-avatar: v-icon call_split
|
||||
@@ -74,8 +76,8 @@
|
||||
v-list-tile(to='/system', v-if='hasPermission(`manage:system`)')
|
||||
v-list-tile-avatar: v-icon tune
|
||||
v-list-tile-title {{ $t('admin:system.title') }}
|
||||
v-list-tile(to='/utilities', v-if='hasPermission(`manage:system`)')
|
||||
v-list-tile-avatar: v-icon build
|
||||
v-list-tile(to='/utilities', v-if='hasPermission(`manage:system`)', disabled)
|
||||
v-list-tile-avatar: v-icon(color='grey lighten-2') build
|
||||
v-list-tile-title {{ $t('admin:utilities.title') }}
|
||||
v-list-tile(to='/dev', v-if='hasPermission([`manage:system`, `manage:api`])')
|
||||
v-list-tile-avatar: v-icon weekend
|
||||
|
@@ -45,7 +45,7 @@
|
||||
:class='isLatestVersion ? "teal lighten-2" : "red lighten-2"'
|
||||
dark
|
||||
)
|
||||
v-btn(fab, absolute, right, top, small, light, to='system')
|
||||
v-btn(fab, absolute, right, top, small, light, to='system', v-if='hasPermission(`manage:system`)')
|
||||
v-icon(v-if='isLatestVersion', color='teal') build
|
||||
v-icon(v-else, color='red darken-4') get_app
|
||||
v-card-text
|
||||
@@ -101,6 +101,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
import AnimatedNumber from 'animated-number-vue'
|
||||
import { get } from 'vuex-pathify'
|
||||
|
||||
@@ -118,10 +119,20 @@ export default {
|
||||
isLatestVersion() {
|
||||
return this.info.currentVersion === this.info.latestVersion
|
||||
},
|
||||
info: get('admin/info')
|
||||
info: get('admin/info'),
|
||||
permissions: get('user/permissions')
|
||||
},
|
||||
methods: {
|
||||
round(val) { return Math.round(val) }
|
||||
round(val) { return Math.round(val) },
|
||||
hasPermission(prm) {
|
||||
if (_.isArray(prm)) {
|
||||
return _.some(prm, p => {
|
||||
return _.includes(this.permissions, p)
|
||||
})
|
||||
} else {
|
||||
return _.includes(this.permissions, prm)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@@ -78,8 +78,8 @@
|
||||
dense
|
||||
)
|
||||
template(slot='selection', slot-scope='{ item, index }')
|
||||
v-chip.white--text.ml-0(v-if='index <= 2', small, label, :color='rule.deny ? `red` : `green`').caption {{ item.value }}
|
||||
v-chip.white--text.ml-0(v-if='index === 3', small, label, :color='rule.deny ? `red lighten-2` : `green lighten-2`').caption + {{ rule.roles.length - 3 }} more
|
||||
v-chip.white--text.ml-0(v-if='index <= 1', small, label, :color='rule.deny ? `red` : `green`').caption {{ item.value }}
|
||||
v-chip.white--text.ml-0(v-if='index === 2', small, label, :color='rule.deny ? `red lighten-2` : `green lighten-2`').caption + {{ rule.roles.length - 2 }} more
|
||||
template(slot='item', slot-scope='props')
|
||||
v-list-tile-action(style='min-width: 30px;')
|
||||
v-checkbox(
|
||||
@@ -163,6 +163,26 @@
|
||||
|
||||
v-btn(icon, @click='removeRule(rule.id)')
|
||||
v-icon(:color='$vuetify.dark ? `grey` : `blue-grey`') clear
|
||||
|
||||
v-divider.mt-3
|
||||
v-subheader.pl-0 Rules Order
|
||||
.body-1.pl-3 Rules are applied in order of path specificity. A more precise path will always override a less defined path.
|
||||
.body-1.pl-4 For example, #[span.teal--text /geography/countries] will override #[span.teal--text /geography].
|
||||
.body-1.pl-3.pt-2 When 2 rules have the same specificity, the priority is given from lowest to highest as follows:
|
||||
.body-1.pl-3.pt-1
|
||||
ul
|
||||
li
|
||||
strong Path Starts With...
|
||||
em.caption.pl-1 (lowest)
|
||||
li
|
||||
strong Path Ends With...
|
||||
li
|
||||
strong Path Matches Regex...
|
||||
li
|
||||
strong Path Is Exactly...
|
||||
em.caption.pl-1 (highest)
|
||||
.body-1.pl-3.pt-2 When 2 rules have the same path specificity AND the same match type, #[strong.red--text DENY] will always override an #[strong.green--text ALLOW] rule.
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -178,16 +198,16 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
roles: [
|
||||
{ text: 'Read Pages', value: 'READ', icon: 'insert_drive_file' },
|
||||
{ text: 'Create Pages', value: 'WRITE', icon: 'insert_drive_file' },
|
||||
{ text: 'Edit + Move Pages', value: 'MANAGE', icon: 'insert_drive_file' },
|
||||
{ text: 'Delete Pages', value: 'DELETE', icon: 'insert_drive_file' },
|
||||
{ text: 'Read / Use Assets', value: 'AS_READ', icon: 'camera' },
|
||||
{ text: 'Upload Assets', value: 'AS_WRITE', icon: 'camera' },
|
||||
{ text: 'Edit + Delete Assets', value: 'AS_MANAGE', icon: 'camera' },
|
||||
{ text: 'Read Comments', value: 'CM_READ', icon: 'insert_comment' },
|
||||
{ text: 'Create Comments', value: 'CM_WRITE', icon: 'insert_comment' },
|
||||
{ text: 'Edit + Delete Comments', value: 'CM_MANAGE', icon: 'insert_comment' }
|
||||
{ text: 'Read Pages', value: 'read:pages', icon: 'insert_drive_file' },
|
||||
{ text: 'Create Pages', value: 'write:pages', icon: 'insert_drive_file' },
|
||||
{ text: 'Edit + Move Pages', value: 'manage:pages', icon: 'insert_drive_file' },
|
||||
{ text: 'Delete Pages', value: 'delete:pages', icon: 'insert_drive_file' },
|
||||
{ text: 'Read / Use Assets', value: 'read:assets', icon: 'camera' },
|
||||
{ text: 'Upload Assets', value: 'write:assets', icon: 'camera' },
|
||||
{ text: 'Edit + Delete Assets', value: 'manage:assets', icon: 'camera' },
|
||||
{ text: 'Read Comments', value: 'read:comments', icon: 'insert_comment' },
|
||||
{ text: 'Create Comments', value: 'write:comments', icon: 'insert_comment' },
|
||||
{ text: 'Edit + Delete Comments', value: 'manage:comments', icon: 'insert_comment' }
|
||||
],
|
||||
matches: [
|
||||
{ text: 'Path Starts With...', value: 'START', icon: '/...' },
|
||||
|
@@ -97,42 +97,38 @@
|
||||
v-btn.btn-animate-rotate(icon, href='/a', slot='activator')
|
||||
v-icon(color='grey') settings
|
||||
span Admin
|
||||
v-menu(offset-y, min-width='300')
|
||||
v-menu(v-if='isAuthenticated', offset-y, min-width='300')
|
||||
v-tooltip(bottom, slot='activator')
|
||||
v-btn.btn-animate-grow(icon, slot='activator', outline, :color='isAuthenticated ? `blue` : `grey darken-3`')
|
||||
v-btn.btn-animate-grow(icon, slot='activator', outline, color='blue')
|
||||
v-icon(color='grey') account_circle
|
||||
span Account
|
||||
v-list.py-0
|
||||
template(v-if='isAuthenticated')
|
||||
v-list-tile.py-3.grey(avatar, :class='$vuetify.dark ? `darken-4-l5` : `lighten-5`')
|
||||
v-list-tile-avatar
|
||||
v-avatar.blue(v-if='picture.kind === `initials`', :size='40')
|
||||
span.white--text.subheading {{picture.initials}}
|
||||
v-avatar(v-else-if='picture.kind === `image`', :size='40')
|
||||
v-img(:src='picture.url')
|
||||
v-list-tile-content
|
||||
v-list-tile-title {{name}}
|
||||
v-list-tile-sub-title {{email}}
|
||||
v-divider.my-0
|
||||
v-list-tile(href='/w')
|
||||
v-list-tile-action: v-icon(color='blue') web
|
||||
v-list-tile-title My Wiki
|
||||
v-divider.my-0
|
||||
v-list-tile(href='/p')
|
||||
v-list-tile-action: v-icon(color='blue') person
|
||||
v-list-tile-title Profile
|
||||
v-divider.my-0
|
||||
v-list-tile(@click='logout')
|
||||
v-list-tile-action: v-icon(color='red') exit_to_app
|
||||
v-list-tile-title Logout
|
||||
template(v-else)
|
||||
v-list-tile(href='/login')
|
||||
v-list-tile-action: v-icon(color='grey') person
|
||||
v-list-tile-title Login
|
||||
v-divider.my-0
|
||||
v-list-tile(href='/register')
|
||||
v-list-tile-action: v-icon(color='grey') person_add
|
||||
v-list-tile-title Register
|
||||
v-list-tile.py-3.grey(avatar, :class='$vuetify.dark ? `darken-4-l5` : `lighten-5`')
|
||||
v-list-tile-avatar
|
||||
v-avatar.blue(v-if='picture.kind === `initials`', :size='40')
|
||||
span.white--text.subheading {{picture.initials}}
|
||||
v-avatar(v-else-if='picture.kind === `image`', :size='40')
|
||||
v-img(:src='picture.url')
|
||||
v-list-tile-content
|
||||
v-list-tile-title {{name}}
|
||||
v-list-tile-sub-title {{email}}
|
||||
v-divider.my-0
|
||||
v-list-tile(href='/w')
|
||||
v-list-tile-action: v-icon(color='blue') web
|
||||
v-list-tile-title My Wiki
|
||||
v-divider.my-0
|
||||
v-list-tile(href='/p')
|
||||
v-list-tile-action: v-icon(color='blue') person
|
||||
v-list-tile-title Profile
|
||||
v-divider.my-0
|
||||
v-list-tile(@click='logout')
|
||||
v-list-tile-action: v-icon(color='red') exit_to_app
|
||||
v-list-tile-title Logout
|
||||
|
||||
v-tooltip(v-else, left)
|
||||
v-btn(icon, slot='activator', outline, color='grey darken-3', href='/login')
|
||||
v-icon(color='grey') account_circle
|
||||
span Login
|
||||
|
||||
page-selector(mode='create', v-model='newPageModal', :open-handler='pageNewCreate')
|
||||
</template>
|
||||
|
Reference in New Issue
Block a user