feat: page Rules access check
This commit is contained in:
parent
75eb277401
commit
7e62c01ed1
@ -7,40 +7,42 @@
|
|||||||
v-list-tile.pt-2(to='/dashboard')
|
v-list-tile.pt-2(to='/dashboard')
|
||||||
v-list-tile-avatar: v-icon dashboard
|
v-list-tile-avatar: v-icon dashboard
|
||||||
v-list-tile-title {{ $t('admin:dashboard.title') }}
|
v-list-tile-title {{ $t('admin:dashboard.title') }}
|
||||||
v-divider.my-2
|
template(v-if='hasPermission([`manage:system`, `manage:navigation`, `write:pages`, `manage:pages`, `delete:pages`])')
|
||||||
v-subheader.pl-4 {{ $t('admin:nav.site') }}
|
v-divider.my-2
|
||||||
v-list-tile(to='/general', v-if='hasPermission(`manage:system`)')
|
v-subheader.pl-4 {{ $t('admin:nav.site') }}
|
||||||
v-list-tile-avatar: v-icon widgets
|
v-list-tile(to='/general', v-if='hasPermission(`manage:system`)')
|
||||||
v-list-tile-title {{ $t('admin:general.title') }}
|
v-list-tile-avatar: v-icon widgets
|
||||||
v-list-tile(to='/locale', v-if='hasPermission(`manage:system`)')
|
v-list-tile-title {{ $t('admin:general.title') }}
|
||||||
v-list-tile-avatar: v-icon language
|
v-list-tile(to='/locale', v-if='hasPermission(`manage:system`)')
|
||||||
v-list-tile-title {{ $t('admin:locale.title') }}
|
v-list-tile-avatar: v-icon language
|
||||||
v-list-tile(to='/navigation', v-if='hasPermission([`manage:system`, `manage:navigation`])')
|
v-list-tile-title {{ $t('admin:locale.title') }}
|
||||||
v-list-tile-avatar: v-icon near_me
|
v-list-tile(to='/navigation', v-if='hasPermission([`manage:system`, `manage:navigation`])')
|
||||||
v-list-tile-title {{ $t('admin:navigation.title') }}
|
v-list-tile-avatar: v-icon near_me
|
||||||
v-list-tile(to='/pages')
|
v-list-tile-title {{ $t('admin:navigation.title') }}
|
||||||
v-list-tile-avatar: v-icon insert_drive_file
|
v-list-tile(to='/pages', v-if='hasPermission([`manage:system`, `write:pages`, `manage:pages`, `delete:pages`])')
|
||||||
v-list-tile-title {{ $t('admin:pages.title') }}
|
v-list-tile-avatar: v-icon insert_drive_file
|
||||||
v-list-tile-action
|
v-list-tile-title {{ $t('admin:pages.title') }}
|
||||||
v-chip(small, disabled, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`')
|
v-list-tile-action
|
||||||
.caption.grey--text {{ info.pagesTotal }}
|
v-chip(small, disabled, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`')
|
||||||
v-list-tile(to='/theme', v-if='hasPermission([`manage:system`, `manage:theme`])')
|
.caption.grey--text {{ info.pagesTotal }}
|
||||||
v-list-tile-avatar: v-icon palette
|
v-list-tile(to='/theme', v-if='hasPermission([`manage:system`, `manage:theme`])')
|
||||||
v-list-tile-title {{ $t('admin:theme.title') }}
|
v-list-tile-avatar: v-icon palette
|
||||||
v-divider.my-2
|
v-list-tile-title {{ $t('admin:theme.title') }}
|
||||||
v-subheader.pl-4 {{ $t('admin:nav.users') }}
|
template(v-if='hasPermission([`manage:system`, `manage:groups`, `write:groups`, `manage:users`, `write:users`])')
|
||||||
v-list-tile(to='/groups')
|
v-divider.my-2
|
||||||
v-list-tile-avatar: v-icon people
|
v-subheader.pl-4 {{ $t('admin:nav.users') }}
|
||||||
v-list-tile-title {{ $t('admin:groups.title') }}
|
v-list-tile(to='/groups', v-if='hasPermission([`manage:system`, `manage:groups`, `write:groups`])')
|
||||||
v-list-tile-action
|
v-list-tile-avatar: v-icon people
|
||||||
v-chip(small, disabled, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`')
|
v-list-tile-title {{ $t('admin:groups.title') }}
|
||||||
.caption.grey--text {{ info.groupsTotal }}
|
v-list-tile-action
|
||||||
v-list-tile(to='/users')
|
v-chip(small, disabled, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`')
|
||||||
v-list-tile-avatar: v-icon perm_identity
|
.caption.grey--text {{ info.groupsTotal }}
|
||||||
v-list-tile-title {{ $t('admin:users.title') }}
|
v-list-tile(to='/users', v-if='hasPermission([`manage:system`, `manage:groups`, `write:groups`, `manage:users`, `write:users`])')
|
||||||
v-list-tile-action
|
v-list-tile-avatar: v-icon perm_identity
|
||||||
v-chip(small, disabled, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`')
|
v-list-tile-title {{ $t('admin:users.title') }}
|
||||||
.caption.grey--text {{ info.usersTotal }}
|
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`)')
|
template(v-if='hasPermission(`manage:system`)')
|
||||||
v-divider.my-2
|
v-divider.my-2
|
||||||
v-subheader.pl-4 {{ $t('admin:nav.modules') }}
|
v-subheader.pl-4 {{ $t('admin:nav.modules') }}
|
||||||
@ -62,8 +64,8 @@
|
|||||||
v-list-tile(to='/storage')
|
v-list-tile(to='/storage')
|
||||||
v-list-tile-avatar: v-icon storage
|
v-list-tile-avatar: v-icon storage
|
||||||
v-list-tile-title {{ $t('admin:storage.title') }}
|
v-list-tile-title {{ $t('admin:storage.title') }}
|
||||||
v-divider.my-2
|
|
||||||
template(v-if='hasPermission([`manage:system`, `manage:api`])')
|
template(v-if='hasPermission([`manage:system`, `manage:api`])')
|
||||||
|
v-divider.my-2
|
||||||
v-subheader.pl-4 {{ $t('admin:nav.system') }}
|
v-subheader.pl-4 {{ $t('admin:nav.system') }}
|
||||||
v-list-tile(to='/api', v-if='hasPermission([`manage:system`, `manage:api`])')
|
v-list-tile(to='/api', v-if='hasPermission([`manage:system`, `manage:api`])')
|
||||||
v-list-tile-avatar: v-icon call_split
|
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(to='/system', v-if='hasPermission(`manage:system`)')
|
||||||
v-list-tile-avatar: v-icon tune
|
v-list-tile-avatar: v-icon tune
|
||||||
v-list-tile-title {{ $t('admin:system.title') }}
|
v-list-tile-title {{ $t('admin:system.title') }}
|
||||||
v-list-tile(to='/utilities', v-if='hasPermission(`manage:system`)')
|
v-list-tile(to='/utilities', v-if='hasPermission(`manage:system`)', disabled)
|
||||||
v-list-tile-avatar: v-icon build
|
v-list-tile-avatar: v-icon(color='grey lighten-2') build
|
||||||
v-list-tile-title {{ $t('admin:utilities.title') }}
|
v-list-tile-title {{ $t('admin:utilities.title') }}
|
||||||
v-list-tile(to='/dev', v-if='hasPermission([`manage:system`, `manage:api`])')
|
v-list-tile(to='/dev', v-if='hasPermission([`manage:system`, `manage:api`])')
|
||||||
v-list-tile-avatar: v-icon weekend
|
v-list-tile-avatar: v-icon weekend
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
:class='isLatestVersion ? "teal lighten-2" : "red lighten-2"'
|
:class='isLatestVersion ? "teal lighten-2" : "red lighten-2"'
|
||||||
dark
|
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-if='isLatestVersion', color='teal') build
|
||||||
v-icon(v-else, color='red darken-4') get_app
|
v-icon(v-else, color='red darken-4') get_app
|
||||||
v-card-text
|
v-card-text
|
||||||
@ -101,6 +101,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import _ from 'lodash'
|
||||||
import AnimatedNumber from 'animated-number-vue'
|
import AnimatedNumber from 'animated-number-vue'
|
||||||
import { get } from 'vuex-pathify'
|
import { get } from 'vuex-pathify'
|
||||||
|
|
||||||
@ -118,10 +119,20 @@ export default {
|
|||||||
isLatestVersion() {
|
isLatestVersion() {
|
||||||
return this.info.currentVersion === this.info.latestVersion
|
return this.info.currentVersion === this.info.latestVersion
|
||||||
},
|
},
|
||||||
info: get('admin/info')
|
info: get('admin/info'),
|
||||||
|
permissions: get('user/permissions')
|
||||||
},
|
},
|
||||||
methods: {
|
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>
|
</script>
|
||||||
|
@ -78,8 +78,8 @@
|
|||||||
dense
|
dense
|
||||||
)
|
)
|
||||||
template(slot='selection', slot-scope='{ item, index }')
|
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 <= 1', 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 === 2', small, label, :color='rule.deny ? `red lighten-2` : `green lighten-2`').caption + {{ rule.roles.length - 2 }} more
|
||||||
template(slot='item', slot-scope='props')
|
template(slot='item', slot-scope='props')
|
||||||
v-list-tile-action(style='min-width: 30px;')
|
v-list-tile-action(style='min-width: 30px;')
|
||||||
v-checkbox(
|
v-checkbox(
|
||||||
@ -163,6 +163,26 @@
|
|||||||
|
|
||||||
v-btn(icon, @click='removeRule(rule.id)')
|
v-btn(icon, @click='removeRule(rule.id)')
|
||||||
v-icon(:color='$vuetify.dark ? `grey` : `blue-grey`') clear
|
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>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -178,16 +198,16 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
roles: [
|
roles: [
|
||||||
{ text: 'Read Pages', value: 'READ', icon: 'insert_drive_file' },
|
{ text: 'Read Pages', value: 'read:pages', icon: 'insert_drive_file' },
|
||||||
{ text: 'Create Pages', value: 'WRITE', icon: 'insert_drive_file' },
|
{ text: 'Create Pages', value: 'write:pages', icon: 'insert_drive_file' },
|
||||||
{ text: 'Edit + Move Pages', value: 'MANAGE', icon: 'insert_drive_file' },
|
{ text: 'Edit + Move Pages', value: 'manage:pages', icon: 'insert_drive_file' },
|
||||||
{ text: 'Delete Pages', value: 'DELETE', icon: 'insert_drive_file' },
|
{ text: 'Delete Pages', value: 'delete:pages', icon: 'insert_drive_file' },
|
||||||
{ text: 'Read / Use Assets', value: 'AS_READ', icon: 'camera' },
|
{ text: 'Read / Use Assets', value: 'read:assets', icon: 'camera' },
|
||||||
{ text: 'Upload Assets', value: 'AS_WRITE', icon: 'camera' },
|
{ text: 'Upload Assets', value: 'write:assets', icon: 'camera' },
|
||||||
{ text: 'Edit + Delete Assets', value: 'AS_MANAGE', icon: 'camera' },
|
{ text: 'Edit + Delete Assets', value: 'manage:assets', icon: 'camera' },
|
||||||
{ text: 'Read Comments', value: 'CM_READ', icon: 'insert_comment' },
|
{ text: 'Read Comments', value: 'read:comments', icon: 'insert_comment' },
|
||||||
{ text: 'Create Comments', value: 'CM_WRITE', icon: 'insert_comment' },
|
{ text: 'Create Comments', value: 'write:comments', icon: 'insert_comment' },
|
||||||
{ text: 'Edit + Delete Comments', value: 'CM_MANAGE', icon: 'insert_comment' }
|
{ text: 'Edit + Delete Comments', value: 'manage:comments', icon: 'insert_comment' }
|
||||||
],
|
],
|
||||||
matches: [
|
matches: [
|
||||||
{ text: 'Path Starts With...', value: 'START', icon: '/...' },
|
{ text: 'Path Starts With...', value: 'START', icon: '/...' },
|
||||||
|
@ -97,42 +97,38 @@
|
|||||||
v-btn.btn-animate-rotate(icon, href='/a', slot='activator')
|
v-btn.btn-animate-rotate(icon, href='/a', slot='activator')
|
||||||
v-icon(color='grey') settings
|
v-icon(color='grey') settings
|
||||||
span Admin
|
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-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
|
v-icon(color='grey') account_circle
|
||||||
span Account
|
span Account
|
||||||
v-list.py-0
|
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.py-3.grey(avatar, :class='$vuetify.dark ? `darken-4-l5` : `lighten-5`')
|
v-list-tile-avatar
|
||||||
v-list-tile-avatar
|
v-avatar.blue(v-if='picture.kind === `initials`', :size='40')
|
||||||
v-avatar.blue(v-if='picture.kind === `initials`', :size='40')
|
span.white--text.subheading {{picture.initials}}
|
||||||
span.white--text.subheading {{picture.initials}}
|
v-avatar(v-else-if='picture.kind === `image`', :size='40')
|
||||||
v-avatar(v-else-if='picture.kind === `image`', :size='40')
|
v-img(:src='picture.url')
|
||||||
v-img(:src='picture.url')
|
v-list-tile-content
|
||||||
v-list-tile-content
|
v-list-tile-title {{name}}
|
||||||
v-list-tile-title {{name}}
|
v-list-tile-sub-title {{email}}
|
||||||
v-list-tile-sub-title {{email}}
|
v-divider.my-0
|
||||||
v-divider.my-0
|
v-list-tile(href='/w')
|
||||||
v-list-tile(href='/w')
|
v-list-tile-action: v-icon(color='blue') web
|
||||||
v-list-tile-action: v-icon(color='blue') web
|
v-list-tile-title My Wiki
|
||||||
v-list-tile-title My Wiki
|
v-divider.my-0
|
||||||
v-divider.my-0
|
v-list-tile(href='/p')
|
||||||
v-list-tile(href='/p')
|
v-list-tile-action: v-icon(color='blue') person
|
||||||
v-list-tile-action: v-icon(color='blue') person
|
v-list-tile-title Profile
|
||||||
v-list-tile-title Profile
|
v-divider.my-0
|
||||||
v-divider.my-0
|
v-list-tile(@click='logout')
|
||||||
v-list-tile(@click='logout')
|
v-list-tile-action: v-icon(color='red') exit_to_app
|
||||||
v-list-tile-action: v-icon(color='red') exit_to_app
|
v-list-tile-title Logout
|
||||||
v-list-tile-title Logout
|
|
||||||
template(v-else)
|
v-tooltip(v-else, left)
|
||||||
v-list-tile(href='/login')
|
v-btn(icon, slot='activator', outline, color='grey darken-3', href='/login')
|
||||||
v-list-tile-action: v-icon(color='grey') person
|
v-icon(color='grey') account_circle
|
||||||
v-list-tile-title Login
|
span 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
|
|
||||||
|
|
||||||
page-selector(mode='create', v-model='newPageModal', :open-handler='pageNewCreate')
|
page-selector(mode='create', v-model='newPageModal', :open-handler='pageNewCreate')
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
query($locale: String!, $namespace: String!) {
|
|
||||||
translations(locale:$locale, namespace:$namespace) {
|
|
||||||
key
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,8 @@
|
|||||||
|
query($locale: String!, $namespace: String!) {
|
||||||
|
localization {
|
||||||
|
translations(locale:$locale, namespace:$namespace) {
|
||||||
|
key
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,7 @@ import _ from 'lodash'
|
|||||||
|
|
||||||
/* global siteConfig, graphQL */
|
/* global siteConfig, graphQL */
|
||||||
|
|
||||||
import localeQuery from 'gql/common/common-locale-query.gql'
|
import localeQuery from 'gql/common/common-localization-query-translations.gql'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
VueI18Next,
|
VueI18Next,
|
||||||
@ -28,8 +28,8 @@ export default {
|
|||||||
}
|
}
|
||||||
}).then(resp => {
|
}).then(resp => {
|
||||||
let ns = {}
|
let ns = {}
|
||||||
if (resp.data.translations.length > 0) {
|
if (_.get(resp, 'data.localization.translations', []).length > 0) {
|
||||||
resp.data.translations.forEach(entry => {
|
resp.data.localization.translations.forEach(entry => {
|
||||||
_.set(ns, entry.key, entry.value)
|
_.set(ns, entry.key, entry.value)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
// @import 'node_modules/diff2html/dist/diff2html.min';
|
// @import 'node_modules/diff2html/dist/diff2html.min';
|
||||||
|
|
||||||
@import 'pages/new';
|
@import 'pages/new';
|
||||||
|
@import 'pages/notfound';
|
||||||
@import 'pages/unauthorized';
|
@import 'pages/unauthorized';
|
||||||
@import 'pages/welcome';
|
@import 'pages/welcome';
|
||||||
@import 'pages/error';
|
@import 'pages/error';
|
||||||
|
81
client/scss/pages/_notfound.scss
Normal file
81
client/scss/pages/_notfound.scss
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
.notfound {
|
||||||
|
background: linear-gradient(to bottom, darken(mc('red', '900'), 25%) 0%, mc('red', '600') 100%);
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: mc('grey', '50');
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
display:block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
background-image: url('../static/svg/motif-circuit.svg');
|
||||||
|
background-position: center center;
|
||||||
|
background-repeat: repeat;
|
||||||
|
background-size: 200px;
|
||||||
|
z-index: 0;
|
||||||
|
opacity: .75;
|
||||||
|
animation: onboardingBgReveal 80s linear infinite;
|
||||||
|
|
||||||
|
@include keyframes(onboardingBgReveal) {
|
||||||
|
0% {
|
||||||
|
background-position-y: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position-y: -2000px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
background-color: transparent;
|
||||||
|
background-image: url('../static/svg/motif-overlay.svg');
|
||||||
|
background-attachment: fixed;
|
||||||
|
background-size: cover;
|
||||||
|
opacity: .5;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 250px;
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
z-index: 2;
|
||||||
|
animation-duration: 2s;
|
||||||
|
|
||||||
|
@include until($tablet) {
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
.v-btn {
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
}
|
@ -147,6 +147,7 @@
|
|||||||
"remove-markdown": "0.3.0",
|
"remove-markdown": "0.3.0",
|
||||||
"request": "2.88.0",
|
"request": "2.88.0",
|
||||||
"request-promise": "4.2.2",
|
"request-promise": "4.2.2",
|
||||||
|
"safe-regex": "2.0.1",
|
||||||
"scim-query-filter-parser": "1.1.0",
|
"scim-query-filter-parser": "1.1.0",
|
||||||
"semver": "5.6.0",
|
"semver": "5.6.0",
|
||||||
"serve-favicon": "2.5.0",
|
"serve-favicon": "2.5.0",
|
||||||
|
@ -131,6 +131,7 @@ router.get('/*', async (req, res, next) => {
|
|||||||
if (pageArgs.path === 'home') {
|
if (pageArgs.path === 'home') {
|
||||||
return res.redirect('/login')
|
return res.redirect('/login')
|
||||||
} else {
|
} else {
|
||||||
|
_.set(res.locals, 'pageMeta.title', 'Unauthorized')
|
||||||
return res.render('unauthorized', { action: 'view'})
|
return res.render('unauthorized', { action: 'view'})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -151,7 +152,11 @@ router.get('/*', async (req, res, next) => {
|
|||||||
res.render('welcome')
|
res.render('welcome')
|
||||||
} else {
|
} else {
|
||||||
_.set(res.locals, 'pageMeta.title', 'Page Not Found')
|
_.set(res.locals, 'pageMeta.title', 'Page Not Found')
|
||||||
res.status(404).render('new', { pagePath: req.path })
|
if (WIKI.auth.checkAccess(req.user, ['write:pages'], pageArgs)) {
|
||||||
|
res.status(404).render('new', { pagePath: req.path })
|
||||||
|
} else {
|
||||||
|
res.render('notfound', { action: 'view'})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ module.exports = {
|
|||||||
guest: {
|
guest: {
|
||||||
cacheExpiration: moment.utc().subtract(1, 'd')
|
cacheExpiration: moment.utc().subtract(1, 'd')
|
||||||
},
|
},
|
||||||
|
groups: {},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the authentication module
|
* Initialize the authentication module
|
||||||
@ -22,23 +23,27 @@ module.exports = {
|
|||||||
init() {
|
init() {
|
||||||
this.passport = passport
|
this.passport = passport
|
||||||
|
|
||||||
passport.serializeUser(function (user, done) {
|
passport.serializeUser((user, done) => {
|
||||||
done(null, user.id)
|
done(null, user.id)
|
||||||
})
|
})
|
||||||
|
|
||||||
passport.deserializeUser(function (id, done) {
|
passport.deserializeUser(async (id, done) => {
|
||||||
WIKI.models.users.query().findById(id).then((user) => {
|
try {
|
||||||
|
const user = await WIKI.models.users.query().findById(id).modifyEager('groups', builder => {
|
||||||
|
builder.select('groups.id', 'permissions')
|
||||||
|
})
|
||||||
if (user) {
|
if (user) {
|
||||||
done(null, user)
|
done(null, user)
|
||||||
} else {
|
} else {
|
||||||
done(new Error(WIKI.lang.t('auth:errors:usernotfound')), null)
|
done(new Error(WIKI.lang.t('auth:errors:usernotfound')), null)
|
||||||
}
|
}
|
||||||
return true
|
} catch (err) {
|
||||||
}).catch((err) => {
|
|
||||||
done(err, null)
|
done(err, null)
|
||||||
})
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.reloadGroups()
|
||||||
|
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -117,13 +122,14 @@ module.exports = {
|
|||||||
res.cookie('jwt', newToken.token, { expires: moment().add(365, 'days').toDate() })
|
res.cookie('jwt', newToken.token, { expires: moment().add(365, 'days').toDate() })
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
WIKI.logger.warn(err)
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// JWT is NOT valid, set as guest
|
// JWT is NOT valid, set as guest
|
||||||
if (!user) {
|
if (!user) {
|
||||||
if (WIKI.auth.guest.cacheExpiration ) {
|
if (true || WIKI.auth.guest.cacheExpiration.isSameOrBefore(moment.utc())) {
|
||||||
WIKI.auth.guest = await WIKI.models.users.getGuestUser()
|
WIKI.auth.guest = await WIKI.models.users.getGuestUser()
|
||||||
WIKI.auth.guest.cacheExpiration = moment.utc().add(1, 'm')
|
WIKI.auth.guest.cacheExpiration = moment.utc().add(1, 'm')
|
||||||
}
|
}
|
||||||
@ -146,18 +152,99 @@ module.exports = {
|
|||||||
* @param {Array<String>} permissions
|
* @param {Array<String>} permissions
|
||||||
* @param {String|Boolean} path
|
* @param {String|Boolean} path
|
||||||
*/
|
*/
|
||||||
checkAccess(user, permissions = [], path = false) {
|
checkAccess(user, permissions = [], page = false) {
|
||||||
// System Admin
|
// System Admin
|
||||||
if (_.includes(user.permissions, 'manage:system')) {
|
if (_.includes(user.permissions, 'manage:system')) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const userPermissions = user.permissions ? user.permissions : user.getGlobalPermissions()
|
||||||
|
|
||||||
// Check Global Permissions
|
// Check Global Permissions
|
||||||
if (_.intersection(user.permissions, permissions).length < 1) {
|
if (_.intersection(userPermissions, permissions).length < 1) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.info('---------------------')
|
||||||
|
|
||||||
// Check Page Rules
|
// Check Page Rules
|
||||||
|
if (path && user.groups) {
|
||||||
|
let checkState = {
|
||||||
|
deny: false,
|
||||||
|
match: false,
|
||||||
|
specificity: ''
|
||||||
|
}
|
||||||
|
user.groups.forEach(grp => {
|
||||||
|
const grpId = _.isObject(grp) ? _.get(grp, 'id', 0) : grp
|
||||||
|
_.get(WIKI.auth.groups, `${grpId}.pageRules`, []).forEach(rule => {
|
||||||
|
console.info(page.path)
|
||||||
|
console.info(rule)
|
||||||
|
switch(rule.match) {
|
||||||
|
case 'START':
|
||||||
|
if (_.startsWith(`/${page.path}`, `/${rule.path}`)) {
|
||||||
|
checkState = this._applyPageRuleSpecificity({ rule, checkState, higherPriority: ['END', 'REGEX', 'EXACT'] })
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'END':
|
||||||
|
if (_.endsWith(page.path, rule.path)) {
|
||||||
|
checkState = this._applyPageRuleSpecificity({ rule, checkState, higherPriority: ['REGEX', 'EXACT'] })
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'REGEX':
|
||||||
|
const reg = new RegExp(rule.path)
|
||||||
|
if (reg.test(page.path)) {
|
||||||
|
checkState = this._applyPageRuleSpecificity({ rule, checkState, higherPriority: ['EXACT'] })
|
||||||
|
}
|
||||||
|
case 'EXACT':
|
||||||
|
if (`/${page.path}` === `/${rule.path}`) {
|
||||||
|
checkState = this._applyPageRuleSpecificity({ rule, checkState, higherPriority: [] })
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
console.info('DAKSJDHKASJD')
|
||||||
|
console.info(checkState)
|
||||||
|
|
||||||
|
return (checkState.match && !checkState.deny)
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check and apply Page Rule specificity
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
*/
|
||||||
|
_applyPageRuleSpecificity ({ rule, checkState, higherPriority = [] }) {
|
||||||
|
if (rule.path.length === checkState.specificity.length) {
|
||||||
|
// Do not override higher priority rules
|
||||||
|
if (_.includes(higherPriority, checkState.match)) {
|
||||||
|
return checkState
|
||||||
|
}
|
||||||
|
// Do not override a previous DENY rule with same match
|
||||||
|
if (rule.match === checkState.match && checkState.deny && !rule.deny) {
|
||||||
|
return checkState
|
||||||
|
}
|
||||||
|
} else if (rule.path.length < checkState.specificity.length) {
|
||||||
|
// Do not override higher specificity rules
|
||||||
|
return checkState
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
deny: rule.deny,
|
||||||
|
match: rule.match,
|
||||||
|
specificity: rule.path
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reload Groups from DB
|
||||||
|
*/
|
||||||
|
async reloadGroups() {
|
||||||
|
const groupsArray = await WIKI.models.groups.query()
|
||||||
|
this.groups = _.keyBy(groupsArray, 'id')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,41 +2,41 @@
|
|||||||
/* global WIKI */
|
/* global WIKI */
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Query: {
|
// Query: {
|
||||||
comments(obj, args, context, info) {
|
// comments(obj, args, context, info) {
|
||||||
return WIKI.models.Comment.findAll({ where: args })
|
// return WIKI.models.Comment.findAll({ where: args })
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
Mutation: {
|
// Mutation: {
|
||||||
createComment(obj, args) {
|
// createComment(obj, args) {
|
||||||
return WIKI.models.Comment.create({
|
// return WIKI.models.Comment.create({
|
||||||
content: args.content,
|
// content: args.content,
|
||||||
author: args.userId,
|
// author: args.userId,
|
||||||
document: args.documentId
|
// document: args.documentId
|
||||||
})
|
// })
|
||||||
},
|
// },
|
||||||
deleteComment(obj, args) {
|
// deleteComment(obj, args) {
|
||||||
return WIKI.models.Comment.destroy({
|
// return WIKI.models.Comment.destroy({
|
||||||
where: {
|
// where: {
|
||||||
id: args.id
|
// id: args.id
|
||||||
},
|
// },
|
||||||
limit: 1
|
// limit: 1
|
||||||
})
|
// })
|
||||||
},
|
// },
|
||||||
modifyComment(obj, args) {
|
// modifyComment(obj, args) {
|
||||||
return WIKI.models.Comment.update({
|
// return WIKI.models.Comment.update({
|
||||||
content: args.content
|
// content: args.content
|
||||||
}, {
|
// }, {
|
||||||
where: { id: args.id }
|
// where: { id: args.id }
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
Comment: {
|
// Comment: {
|
||||||
author(cm) {
|
// author(cm) {
|
||||||
return cm.getAuthor()
|
// return cm.getAuthor()
|
||||||
},
|
// },
|
||||||
document(cm) {
|
// document(cm) {
|
||||||
return cm.getDocument()
|
// return cm.getDocument()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
|
|
||||||
/* global WIKI */
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
Query: {
|
|
||||||
documents(obj, args, context, info) {
|
|
||||||
return WIKI.models.Document.findAll({ where: args })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Mutation: {
|
|
||||||
createDocument(obj, args) {
|
|
||||||
return WIKI.models.Document.create(args)
|
|
||||||
},
|
|
||||||
deleteDocument(obj, args) {
|
|
||||||
return WIKI.models.Document.destroy({
|
|
||||||
where: {
|
|
||||||
id: args.id
|
|
||||||
},
|
|
||||||
limit: 1
|
|
||||||
})
|
|
||||||
},
|
|
||||||
modifyDocument(obj, args) {
|
|
||||||
return WIKI.models.Document.update({
|
|
||||||
title: args.title,
|
|
||||||
subtitle: args.subtitle
|
|
||||||
}, {
|
|
||||||
where: { id: args.id }
|
|
||||||
})
|
|
||||||
},
|
|
||||||
moveDocument(obj, args) {
|
|
||||||
return WIKI.models.Document.update({
|
|
||||||
path: args.path
|
|
||||||
}, {
|
|
||||||
where: { id: args.id }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Document: {
|
|
||||||
comments(doc) {
|
|
||||||
return doc.getComments()
|
|
||||||
},
|
|
||||||
tags(doc) {
|
|
||||||
return doc.getTags()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,48 +4,48 @@
|
|||||||
const gql = require('graphql')
|
const gql = require('graphql')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Query: {
|
// Query: {
|
||||||
files(obj, args, context, info) {
|
// files(obj, args, context, info) {
|
||||||
return WIKI.models.File.findAll({ where: args })
|
// return WIKI.models.File.findAll({ where: args })
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
Mutation: {
|
// Mutation: {
|
||||||
uploadFile(obj, args) {
|
// uploadFile(obj, args) {
|
||||||
// todo
|
// // todo
|
||||||
return WIKI.models.File.create(args)
|
// return WIKI.models.File.create(args)
|
||||||
},
|
// },
|
||||||
deleteFile(obj, args) {
|
// deleteFile(obj, args) {
|
||||||
return WIKI.models.File.destroy({
|
// return WIKI.models.File.destroy({
|
||||||
where: {
|
// where: {
|
||||||
id: args.id
|
// id: args.id
|
||||||
},
|
// },
|
||||||
limit: 1
|
// limit: 1
|
||||||
})
|
// })
|
||||||
},
|
// },
|
||||||
renameFile(obj, args) {
|
// renameFile(obj, args) {
|
||||||
return WIKI.models.File.update({
|
// return WIKI.models.File.update({
|
||||||
filename: args.filename
|
// filename: args.filename
|
||||||
}, {
|
// }, {
|
||||||
where: { id: args.id }
|
// where: { id: args.id }
|
||||||
})
|
// })
|
||||||
},
|
// },
|
||||||
moveFile(obj, args) {
|
// moveFile(obj, args) {
|
||||||
return WIKI.models.File.findById(args.fileId).then(fl => {
|
// return WIKI.models.File.findById(args.fileId).then(fl => {
|
||||||
if (!fl) {
|
// if (!fl) {
|
||||||
throw new gql.GraphQLError('Invalid File ID')
|
// throw new gql.GraphQLError('Invalid File ID')
|
||||||
}
|
// }
|
||||||
return WIKI.models.Folder.findById(args.folderId).then(fld => {
|
// return WIKI.models.Folder.findById(args.folderId).then(fld => {
|
||||||
if (!fld) {
|
// if (!fld) {
|
||||||
throw new gql.GraphQLError('Invalid Folder ID')
|
// throw new gql.GraphQLError('Invalid Folder ID')
|
||||||
}
|
// }
|
||||||
return fl.setFolder(fld)
|
// return fl.setFolder(fld)
|
||||||
})
|
// })
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
File: {
|
// File: {
|
||||||
folder(fl) {
|
// folder(fl) {
|
||||||
return fl.getFolder()
|
// return fl.getFolder()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
@ -2,34 +2,34 @@
|
|||||||
/* global WIKI */
|
/* global WIKI */
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Query: {
|
// Query: {
|
||||||
folders(obj, args, context, info) {
|
// folders(obj, args, context, info) {
|
||||||
return WIKI.models.Folder.findAll({ where: args })
|
// return WIKI.models.Folder.findAll({ where: args })
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
Mutation: {
|
// Mutation: {
|
||||||
createFolder(obj, args) {
|
// createFolder(obj, args) {
|
||||||
return WIKI.models.Folder.create(args)
|
// return WIKI.models.Folder.create(args)
|
||||||
},
|
// },
|
||||||
deleteFolder(obj, args) {
|
// deleteFolder(obj, args) {
|
||||||
return WIKI.models.Folder.destroy({
|
// return WIKI.models.Folder.destroy({
|
||||||
where: {
|
// where: {
|
||||||
id: args.id
|
// id: args.id
|
||||||
},
|
// },
|
||||||
limit: 1
|
// limit: 1
|
||||||
})
|
// })
|
||||||
},
|
// },
|
||||||
renameFolder(obj, args) {
|
// renameFolder(obj, args) {
|
||||||
return WIKI.models.Folder.update({
|
// return WIKI.models.Folder.update({
|
||||||
name: args.name
|
// name: args.name
|
||||||
}, {
|
// }, {
|
||||||
where: { id: args.id }
|
// where: { id: args.id }
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
Folder: {
|
// Folder: {
|
||||||
files(grp) {
|
// files(grp) {
|
||||||
return grp.getFiles()
|
// return grp.getFiles()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
const graphHelper = require('../../helpers/graph')
|
const graphHelper = require('../../helpers/graph')
|
||||||
|
const safeRegex = require('safe-regex')
|
||||||
|
|
||||||
/* global WIKI */
|
/* global WIKI */
|
||||||
|
|
||||||
@ -44,6 +45,7 @@ module.exports = {
|
|||||||
pageRules: JSON.stringify([]),
|
pageRules: JSON.stringify([]),
|
||||||
isSystem: false
|
isSystem: false
|
||||||
})
|
})
|
||||||
|
await WIKI.auth.reloadGroups()
|
||||||
return {
|
return {
|
||||||
responseResult: graphHelper.generateSuccess('Group created successfully.'),
|
responseResult: graphHelper.generateSuccess('Group created successfully.'),
|
||||||
group
|
group
|
||||||
@ -51,6 +53,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
async delete(obj, args) {
|
async delete(obj, args) {
|
||||||
await WIKI.models.groups.query().deleteById(args.id)
|
await WIKI.models.groups.query().deleteById(args.id)
|
||||||
|
await WIKI.auth.reloadGroups()
|
||||||
return {
|
return {
|
||||||
responseResult: graphHelper.generateSuccess('Group has been deleted.')
|
responseResult: graphHelper.generateSuccess('Group has been deleted.')
|
||||||
}
|
}
|
||||||
@ -70,11 +73,20 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async update(obj, args) {
|
async update(obj, args) {
|
||||||
|
if(_.some(args.pageRules, pr => {
|
||||||
|
return pr.match !== 'REGEX' || safeRegex(pr.path)
|
||||||
|
})) {
|
||||||
|
throw new gql.GraphQLError('Some Page Rules contains unsafe or exponential time regex.')
|
||||||
|
}
|
||||||
|
|
||||||
await WIKI.models.groups.query().patch({
|
await WIKI.models.groups.query().patch({
|
||||||
name: args.name,
|
name: args.name,
|
||||||
permissions: JSON.stringify(args.permissions),
|
permissions: JSON.stringify(args.permissions),
|
||||||
pageRules: JSON.stringify(args.pageRules)
|
pageRules: JSON.stringify(args.pageRules)
|
||||||
}).where('id', args.id)
|
}).where('id', args.id)
|
||||||
|
|
||||||
|
await WIKI.auth.reloadGroups()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
responseResult: graphHelper.generateSuccess('Group has been updated.')
|
responseResult: graphHelper.generateSuccess('Group has been updated.')
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,9 @@ module.exports = {
|
|||||||
namespacing: WIKI.config.lang.namespacing,
|
namespacing: WIKI.config.lang.namespacing,
|
||||||
namespaces: WIKI.config.lang.namespaces
|
namespaces: WIKI.config.lang.namespaces
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
translations (obj, args, context, info) {
|
||||||
|
return WIKI.lang.getByNamespace(args.locale, args.namespace)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
LocalizationMutation: {
|
LocalizationMutation: {
|
||||||
|
@ -16,15 +16,6 @@ module.exports = {
|
|||||||
offsetPage: args.offsetPage || 0,
|
offsetPage: args.offsetPage || 0,
|
||||||
offsetSize: args.offsetSize || 100
|
offsetSize: args.offsetSize || 100
|
||||||
})
|
})
|
||||||
},
|
|
||||||
async list(obj, args, context, info) {
|
|
||||||
return WIKI.models.pages.query().select(
|
|
||||||
'pages.*',
|
|
||||||
WIKI.models.pages.relatedQuery('users').count().as('userCount')
|
|
||||||
)
|
|
||||||
},
|
|
||||||
async single(obj, args, context, info) {
|
|
||||||
return WIKI.models.pages.query().findById(args.id)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
PageMutation: {
|
PageMutation: {
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
|
|
||||||
/* global WIKI */
|
|
||||||
|
|
||||||
const gql = require('graphql')
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
Query: {
|
|
||||||
rights(obj, args, context, info) {
|
|
||||||
return WIKI.models.Right.findAll({ where: args })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Mutation: {
|
|
||||||
addRightToGroup(obj, args) {
|
|
||||||
return WIKI.models.Group.findById(args.groupId).then(grp => {
|
|
||||||
if (!grp) {
|
|
||||||
throw new gql.GraphQLError('Invalid Group ID')
|
|
||||||
}
|
|
||||||
return WIKI.models.Right.create({
|
|
||||||
path: args.path,
|
|
||||||
role: args.role,
|
|
||||||
exact: args.exact,
|
|
||||||
allow: args.allow,
|
|
||||||
group: grp
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
removeRightFromGroup(obj, args) {
|
|
||||||
return WIKI.models.Right.destroy({
|
|
||||||
where: {
|
|
||||||
id: args.rightId
|
|
||||||
},
|
|
||||||
limit: 1
|
|
||||||
})
|
|
||||||
},
|
|
||||||
modifyRight(obj, args) {
|
|
||||||
return WIKI.models.Right.update({
|
|
||||||
path: args.path,
|
|
||||||
role: args.role,
|
|
||||||
exact: args.exact,
|
|
||||||
allow: args.allow
|
|
||||||
}, {
|
|
||||||
where: {
|
|
||||||
id: args.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Right: {
|
|
||||||
group(rt) {
|
|
||||||
return rt.getGroup()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
|
|
||||||
/* global WIKI */
|
|
||||||
|
|
||||||
const _ = require('lodash')
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
Query: {
|
|
||||||
settings(obj, args, context, info) {
|
|
||||||
return WIKI.models.Setting.findAll({ where: args, raw: true }).then(entries => {
|
|
||||||
return _.map(entries, entry => {
|
|
||||||
entry.config = JSON.stringify(entry.config)
|
|
||||||
return entry
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Mutation: {
|
|
||||||
setConfigEntry(obj, args) {
|
|
||||||
return WIKI.models.Setting.update({
|
|
||||||
value: args.value
|
|
||||||
}, { where: { key: args.key } })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -20,13 +20,9 @@ module.exports = {
|
|||||||
Query: {
|
Query: {
|
||||||
async system() { return {} }
|
async system() { return {} }
|
||||||
},
|
},
|
||||||
Mutation: {
|
|
||||||
async system() { return {} }
|
|
||||||
},
|
|
||||||
SystemQuery: {
|
SystemQuery: {
|
||||||
async info() { return {} }
|
async info() { return {} }
|
||||||
},
|
},
|
||||||
SystemMutation: { },
|
|
||||||
SystemInfo: {
|
SystemInfo: {
|
||||||
configFile() {
|
configFile() {
|
||||||
return path.join(process.cwd(), 'config.yml')
|
return path.join(process.cwd(), 'config.yml')
|
||||||
|
@ -4,60 +4,60 @@
|
|||||||
const gql = require('graphql')
|
const gql = require('graphql')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Query: {
|
// Query: {
|
||||||
tags(obj, args, context, info) {
|
// tags(obj, args, context, info) {
|
||||||
return WIKI.models.Tag.findAll({ where: args })
|
// return WIKI.models.Tag.findAll({ where: args })
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
Mutation: {
|
// Mutation: {
|
||||||
assignTagToDocument(obj, args) {
|
// assignTagToDocument(obj, args) {
|
||||||
return WIKI.models.Tag.findById(args.tagId).then(tag => {
|
// return WIKI.models.Tag.findById(args.tagId).then(tag => {
|
||||||
if (!tag) {
|
// if (!tag) {
|
||||||
throw new gql.GraphQLError('Invalid Tag ID')
|
// throw new gql.GraphQLError('Invalid Tag ID')
|
||||||
}
|
// }
|
||||||
return WIKI.models.Document.findById(args.documentId).then(doc => {
|
// return WIKI.models.Document.findById(args.documentId).then(doc => {
|
||||||
if (!doc) {
|
// if (!doc) {
|
||||||
throw new gql.GraphQLError('Invalid Document ID')
|
// throw new gql.GraphQLError('Invalid Document ID')
|
||||||
}
|
// }
|
||||||
return tag.addDocument(doc)
|
// return tag.addDocument(doc)
|
||||||
})
|
// })
|
||||||
})
|
// })
|
||||||
},
|
// },
|
||||||
createTag(obj, args) {
|
// createTag(obj, args) {
|
||||||
return WIKI.models.Tag.create(args)
|
// return WIKI.models.Tag.create(args)
|
||||||
},
|
// },
|
||||||
deleteTag(obj, args) {
|
// deleteTag(obj, args) {
|
||||||
return WIKI.models.Tag.destroy({
|
// return WIKI.models.Tag.destroy({
|
||||||
where: {
|
// where: {
|
||||||
id: args.id
|
// id: args.id
|
||||||
},
|
// },
|
||||||
limit: 1
|
// limit: 1
|
||||||
})
|
// })
|
||||||
},
|
// },
|
||||||
removeTagFromDocument(obj, args) {
|
// removeTagFromDocument(obj, args) {
|
||||||
return WIKI.models.Tag.findById(args.tagId).then(tag => {
|
// return WIKI.models.Tag.findById(args.tagId).then(tag => {
|
||||||
if (!tag) {
|
// if (!tag) {
|
||||||
throw new gql.GraphQLError('Invalid Tag ID')
|
// throw new gql.GraphQLError('Invalid Tag ID')
|
||||||
}
|
// }
|
||||||
return WIKI.models.Document.findById(args.documentId).then(doc => {
|
// return WIKI.models.Document.findById(args.documentId).then(doc => {
|
||||||
if (!doc) {
|
// if (!doc) {
|
||||||
throw new gql.GraphQLError('Invalid Document ID')
|
// throw new gql.GraphQLError('Invalid Document ID')
|
||||||
}
|
// }
|
||||||
return tag.removeDocument(doc)
|
// return tag.removeDocument(doc)
|
||||||
})
|
// })
|
||||||
})
|
// })
|
||||||
},
|
// },
|
||||||
renameTag(obj, args) {
|
// renameTag(obj, args) {
|
||||||
return WIKI.models.Group.update({
|
// return WIKI.models.Group.update({
|
||||||
key: args.key
|
// key: args.key
|
||||||
}, {
|
// }, {
|
||||||
where: { id: args.id }
|
// where: { id: args.id }
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
Tag: {
|
// Tag: {
|
||||||
documents(tag) {
|
// documents(tag) {
|
||||||
return tag.getDocuments()
|
// return tag.getDocuments()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
|
|
||||||
/* global WIKI */
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
Query: {
|
|
||||||
translations (obj, args, context, info) {
|
|
||||||
return WIKI.lang.getByNamespace(args.locale, args.namespace)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Mutation: {},
|
|
||||||
Translation: {}
|
|
||||||
}
|
|
@ -22,7 +22,6 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
async single(obj, args, context, info) {
|
async single(obj, args, context, info) {
|
||||||
let usr = await WIKI.models.users.query().findById(args.id)
|
let usr = await WIKI.models.users.query().findById(args.id)
|
||||||
console.info(usr)
|
|
||||||
usr.password = ''
|
usr.password = ''
|
||||||
usr.tfaSecret = ''
|
usr.tfaSecret = ''
|
||||||
return usr
|
return usr
|
||||||
|
@ -1,37 +1,32 @@
|
|||||||
|
# ====================== #
|
||||||
|
# Wiki.js GraphQL Schema #
|
||||||
# ENUMS
|
# ====================== #
|
||||||
|
|
||||||
enum FileType {
|
|
||||||
binary
|
|
||||||
image
|
|
||||||
}
|
|
||||||
|
|
||||||
enum RightRole {
|
|
||||||
read
|
|
||||||
write
|
|
||||||
manage
|
|
||||||
}
|
|
||||||
|
|
||||||
# DIRECTIVES
|
# DIRECTIVES
|
||||||
|
# ----------
|
||||||
|
|
||||||
directive @auth(requires: [String]) on QUERY | FIELD_DEFINITION | ARGUMENT_DEFINITION
|
directive @auth(requires: [String]) on QUERY | FIELD_DEFINITION | ARGUMENT_DEFINITION
|
||||||
|
|
||||||
# TYPES
|
# TYPES
|
||||||
|
# -----
|
||||||
|
|
||||||
|
# Generic Key Value Pair
|
||||||
type KeyValuePair {
|
type KeyValuePair {
|
||||||
key: String!
|
key: String!
|
||||||
value: String!
|
value: String!
|
||||||
}
|
}
|
||||||
|
# General Key Value Pair Input
|
||||||
input KeyValuePairInput {
|
input KeyValuePairInput {
|
||||||
key: String!
|
key: String!
|
||||||
value: String!
|
value: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Generic Mutation Response
|
||||||
type DefaultResponse {
|
type DefaultResponse {
|
||||||
responseResult: ResponseStatus
|
responseResult: ResponseStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Mutation Status
|
||||||
type ResponseStatus {
|
type ResponseStatus {
|
||||||
succeeded: Boolean!
|
succeeded: Boolean!
|
||||||
errorCode: Int!
|
errorCode: Int!
|
||||||
@ -39,220 +34,14 @@ type ResponseStatus {
|
|||||||
message: String
|
message: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type Comment {
|
# ROOT
|
||||||
id: Int!
|
# ----
|
||||||
createdAt: Date
|
|
||||||
updatedAt: Date
|
|
||||||
content: String
|
|
||||||
document: Document!
|
|
||||||
author: User!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Document {
|
|
||||||
id: Int!
|
|
||||||
createdAt: Date
|
|
||||||
updatedAt: Date
|
|
||||||
path: String!
|
|
||||||
title: String!
|
|
||||||
subtitle: String
|
|
||||||
parentPath: String
|
|
||||||
parentTitle: String
|
|
||||||
isDirectory: Boolean!
|
|
||||||
isEntry: Boolean!
|
|
||||||
searchContent: String
|
|
||||||
comments: [Comment]
|
|
||||||
tags: [Tag]
|
|
||||||
}
|
|
||||||
|
|
||||||
type File {
|
|
||||||
id: Int!
|
|
||||||
createdAt: Date
|
|
||||||
updatedAt: Date
|
|
||||||
category: FileType!
|
|
||||||
mime: String!
|
|
||||||
extra: String
|
|
||||||
filename: String!
|
|
||||||
basename: String!
|
|
||||||
filesize: Int!
|
|
||||||
folder: Folder
|
|
||||||
}
|
|
||||||
|
|
||||||
type Folder {
|
|
||||||
id: Int!
|
|
||||||
createdAt: Date
|
|
||||||
updatedAt: Date
|
|
||||||
name: String!
|
|
||||||
files: [File]
|
|
||||||
}
|
|
||||||
|
|
||||||
type Right {
|
|
||||||
id: Int!
|
|
||||||
createdAt: Date
|
|
||||||
updatedAt: Date
|
|
||||||
path: String!
|
|
||||||
role: RightRole!
|
|
||||||
exact: Boolean!
|
|
||||||
allow: Boolean!
|
|
||||||
group: Group!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Setting {
|
|
||||||
id: Int!
|
|
||||||
createdAt: Date
|
|
||||||
updatedAt: Date
|
|
||||||
key: String!
|
|
||||||
config: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
# Tags are attached to one or more documents
|
|
||||||
type Tag {
|
|
||||||
id: Int!
|
|
||||||
createdAt: Date
|
|
||||||
updatedAt: Date
|
|
||||||
key: String!
|
|
||||||
documents: [Document]
|
|
||||||
}
|
|
||||||
|
|
||||||
type Translation {
|
|
||||||
key: String!
|
|
||||||
value: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
type OperationResult {
|
|
||||||
succeeded: Boolean!
|
|
||||||
message: String
|
|
||||||
data: String
|
|
||||||
}
|
|
||||||
|
|
||||||
# Query (Read)
|
# Query (Read)
|
||||||
type Query {
|
type Query
|
||||||
comments(id: Int): [Comment]
|
|
||||||
documents(id: Int, path: String): [Document]
|
|
||||||
files(id: Int): [File]
|
|
||||||
folders(id: Int, name: String): [Folder]
|
|
||||||
rights(id: Int): [Right]
|
|
||||||
settings(key: String): [Setting]
|
|
||||||
tags(key: String): [Tag]
|
|
||||||
translations(locale: String!, namespace: String!): [Translation]
|
|
||||||
}
|
|
||||||
|
|
||||||
# Mutations (Create, Update, Delete)
|
# Mutations (Create, Update, Delete)
|
||||||
type Mutation {
|
type Mutation
|
||||||
addRightToGroup(
|
|
||||||
groupId: Int!
|
|
||||||
path: String!
|
|
||||||
role: RightRole!
|
|
||||||
exact: Boolean!
|
|
||||||
allow: Boolean!
|
|
||||||
): Right
|
|
||||||
|
|
||||||
assignTagToDocument(
|
|
||||||
tagId: Int!
|
|
||||||
documentId: Int!
|
|
||||||
): OperationResult
|
|
||||||
|
|
||||||
createComment(
|
|
||||||
userId: Int!
|
|
||||||
documentId: Int!
|
|
||||||
content: String!
|
|
||||||
): Comment
|
|
||||||
|
|
||||||
createDocument(
|
|
||||||
path: String!
|
|
||||||
title: String!
|
|
||||||
subtitle: String
|
|
||||||
): Document
|
|
||||||
|
|
||||||
createFolder(
|
|
||||||
name: String!
|
|
||||||
): Folder
|
|
||||||
|
|
||||||
createTag(
|
|
||||||
name: String!
|
|
||||||
): Tag
|
|
||||||
|
|
||||||
deleteComment(
|
|
||||||
id: Int!
|
|
||||||
): OperationResult
|
|
||||||
|
|
||||||
deleteDocument(
|
|
||||||
id: Int!
|
|
||||||
): OperationResult
|
|
||||||
|
|
||||||
deleteFile(
|
|
||||||
id: Int!
|
|
||||||
): OperationResult
|
|
||||||
|
|
||||||
deleteFolder(
|
|
||||||
id: Int!
|
|
||||||
): OperationResult
|
|
||||||
|
|
||||||
deleteTag(
|
|
||||||
id: Int!
|
|
||||||
): OperationResult
|
|
||||||
|
|
||||||
modifyComment(
|
|
||||||
id: Int!
|
|
||||||
content: String!
|
|
||||||
): Document
|
|
||||||
|
|
||||||
modifyDocument(
|
|
||||||
id: Int!
|
|
||||||
title: String
|
|
||||||
subtitle: String
|
|
||||||
): Document
|
|
||||||
|
|
||||||
modifyRight(
|
|
||||||
id: Int!
|
|
||||||
path: String
|
|
||||||
role: RightRole
|
|
||||||
exact: Boolean
|
|
||||||
allow: Boolean
|
|
||||||
): Right
|
|
||||||
|
|
||||||
moveDocument(
|
|
||||||
id: Int!
|
|
||||||
path: String!
|
|
||||||
): OperationResult
|
|
||||||
|
|
||||||
moveFile(
|
|
||||||
id: Int!
|
|
||||||
folderId: Int!
|
|
||||||
): OperationResult
|
|
||||||
|
|
||||||
renameFile(
|
|
||||||
id: Int!
|
|
||||||
name: String!
|
|
||||||
): OperationResult
|
|
||||||
|
|
||||||
renameFolder(
|
|
||||||
id: Int!
|
|
||||||
name: String!
|
|
||||||
): OperationResult
|
|
||||||
|
|
||||||
renameTag(
|
|
||||||
id: Int!
|
|
||||||
key: String!
|
|
||||||
): OperationResult
|
|
||||||
|
|
||||||
removeTagFromDocument(
|
|
||||||
tagId: Int!
|
|
||||||
documentId: Int!
|
|
||||||
): OperationResult
|
|
||||||
|
|
||||||
removeRightFromGroup(
|
|
||||||
rightId: Int!
|
|
||||||
): OperationResult
|
|
||||||
|
|
||||||
setConfigEntry(
|
|
||||||
key: String!
|
|
||||||
value: String!
|
|
||||||
): OperationResult
|
|
||||||
|
|
||||||
uploadFile(
|
|
||||||
category: FileType!
|
|
||||||
filename: String!
|
|
||||||
): File
|
|
||||||
}
|
|
||||||
|
|
||||||
|
# Subscriptions (Push, Real-time)
|
||||||
type Subscription
|
type Subscription
|
||||||
|
@ -89,7 +89,7 @@ type PageRule {
|
|||||||
id: String!
|
id: String!
|
||||||
deny: Boolean!
|
deny: Boolean!
|
||||||
match: PageRuleMatch!
|
match: PageRuleMatch!
|
||||||
roles: [PageRuleRole]!
|
roles: [String]!
|
||||||
path: String!
|
path: String!
|
||||||
locales: [String]!
|
locales: [String]!
|
||||||
}
|
}
|
||||||
@ -98,24 +98,11 @@ input PageRuleInput {
|
|||||||
id: String!
|
id: String!
|
||||||
deny: Boolean!
|
deny: Boolean!
|
||||||
match: PageRuleMatch!
|
match: PageRuleMatch!
|
||||||
roles: [PageRuleRole]!
|
roles: [String]!
|
||||||
path: String!
|
path: String!
|
||||||
locales: [String]!
|
locales: [String]!
|
||||||
}
|
}
|
||||||
|
|
||||||
enum PageRuleRole {
|
|
||||||
READ
|
|
||||||
WRITE
|
|
||||||
MANAGE
|
|
||||||
DELETE
|
|
||||||
AS_READ
|
|
||||||
AS_WRITE
|
|
||||||
AS_MANAGE
|
|
||||||
CM_READ
|
|
||||||
CM_WRITE
|
|
||||||
CM_MANAGE
|
|
||||||
}
|
|
||||||
|
|
||||||
enum PageRuleMatch {
|
enum PageRuleMatch {
|
||||||
START
|
START
|
||||||
EXACT
|
EXACT
|
||||||
|
@ -17,6 +17,7 @@ extend type Mutation {
|
|||||||
type LocalizationQuery {
|
type LocalizationQuery {
|
||||||
locales: [LocalizationLocale]
|
locales: [LocalizationLocale]
|
||||||
config: LocalizationConfig
|
config: LocalizationConfig
|
||||||
|
translations(locale: String!, namespace: String!): [Translation]
|
||||||
}
|
}
|
||||||
|
|
||||||
# -----------------------------------------------
|
# -----------------------------------------------
|
||||||
@ -57,3 +58,8 @@ type LocalizationConfig {
|
|||||||
namespacing: Boolean!
|
namespacing: Boolean!
|
||||||
namespaces: [String]!
|
namespaces: [String]!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Translation {
|
||||||
|
key: String!
|
||||||
|
value: String!
|
||||||
|
}
|
||||||
|
@ -19,19 +19,7 @@ type PageQuery {
|
|||||||
id: Int!
|
id: Int!
|
||||||
offsetPage: Int
|
offsetPage: Int
|
||||||
offsetSize: Int
|
offsetSize: Int
|
||||||
): PageHistoryResult
|
): PageHistoryResult @auth(requires: ["manage:system", "read:pages"])
|
||||||
|
|
||||||
list(
|
|
||||||
filter: String
|
|
||||||
orderBy: String
|
|
||||||
): [PageMinimal]
|
|
||||||
|
|
||||||
single(
|
|
||||||
id: Int
|
|
||||||
path: String
|
|
||||||
locale: String
|
|
||||||
isPrivate: Boolean
|
|
||||||
): Page
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# -----------------------------------------------
|
# -----------------------------------------------
|
||||||
@ -82,21 +70,8 @@ type PageResponse {
|
|||||||
page: Page
|
page: Page
|
||||||
}
|
}
|
||||||
|
|
||||||
type PageMinimal {
|
|
||||||
id: Int!
|
|
||||||
name: String!
|
|
||||||
userCount: Int
|
|
||||||
createdAt: Date!
|
|
||||||
updatedAt: Date!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Page {
|
type Page {
|
||||||
id: Int!
|
id: Int!
|
||||||
name: String!
|
|
||||||
rights: [Right]
|
|
||||||
users: [User]
|
|
||||||
createdAt: Date!
|
|
||||||
updatedAt: Date!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type PageHistory {
|
type PageHistory {
|
||||||
|
@ -49,7 +49,7 @@ type SiteConfig {
|
|||||||
description: String!
|
description: String!
|
||||||
robots: [String]!
|
robots: [String]!
|
||||||
analyticsService: String!
|
analyticsService: String!
|
||||||
analyticsId: String!
|
analyticsId: String!
|
||||||
company: String!
|
company: String!
|
||||||
hasLogo: Boolean!
|
hasLogo: Boolean!
|
||||||
logoIsSquare: Boolean!
|
logoIsSquare: Boolean!
|
||||||
|
@ -6,50 +6,42 @@ extend type Query {
|
|||||||
system: SystemQuery
|
system: SystemQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
extend type Mutation {
|
|
||||||
system: SystemMutation
|
|
||||||
}
|
|
||||||
|
|
||||||
# -----------------------------------------------
|
# -----------------------------------------------
|
||||||
# QUERIES
|
# QUERIES
|
||||||
# -----------------------------------------------
|
# -----------------------------------------------
|
||||||
|
|
||||||
type SystemQuery {
|
type SystemQuery {
|
||||||
info: SystemInfo @auth(requires: ["manage:system"])
|
info: SystemInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
# -----------------------------------------------
|
# -----------------------------------------------
|
||||||
# MUTATIONS
|
# MUTATIONS
|
||||||
# -----------------------------------------------
|
# -----------------------------------------------
|
||||||
|
|
||||||
type SystemMutation {
|
|
||||||
todo: String
|
|
||||||
}
|
|
||||||
|
|
||||||
# -----------------------------------------------
|
# -----------------------------------------------
|
||||||
# TYPES
|
# TYPES
|
||||||
# -----------------------------------------------
|
# -----------------------------------------------
|
||||||
|
|
||||||
type SystemInfo {
|
type SystemInfo {
|
||||||
configFile: String
|
configFile: String @auth(requires: ["manage:system"])
|
||||||
cpuCores: Int
|
cpuCores: Int @auth(requires: ["manage:system"])
|
||||||
currentVersion: String
|
currentVersion: String @auth(requires: ["manage:system"])
|
||||||
dbHost: String
|
dbHost: String @auth(requires: ["manage:system"])
|
||||||
dbType: String
|
dbType: String @auth(requires: ["manage:system"])
|
||||||
dbVersion: String
|
dbVersion: String @auth(requires: ["manage:system"])
|
||||||
groupsTotal: Int
|
groupsTotal: Int @auth(requires: ["manage:system", "manage:navigation", "manage:groups", "write:groups", "manage:users", "write:users"])
|
||||||
hostname: String
|
hostname: String @auth(requires: ["manage:system"])
|
||||||
latestVersion: String
|
latestVersion: String @auth(requires: ["manage:system"])
|
||||||
latestVersionReleaseDate: Date
|
latestVersionReleaseDate: Date @auth(requires: ["manage:system"])
|
||||||
nodeVersion: String
|
nodeVersion: String @auth(requires: ["manage:system"])
|
||||||
operatingSystem: String
|
operatingSystem: String @auth(requires: ["manage:system"])
|
||||||
pagesTotal: Int
|
pagesTotal: Int @auth(requires: ["manage:system", "manage:navigation", "manage:pages", "delete:pages"])
|
||||||
platform: String
|
platform: String @auth(requires: ["manage:system"])
|
||||||
ramTotal: String
|
ramTotal: String @auth(requires: ["manage:system"])
|
||||||
redisHost: String
|
redisHost: String @auth(requires: ["manage:system"])
|
||||||
redisTotalRAM: String
|
redisTotalRAM: String @auth(requires: ["manage:system"])
|
||||||
redisUsedRAM: String
|
redisUsedRAM: String @auth(requires: ["manage:system"])
|
||||||
redisVersion: String
|
redisVersion: String @auth(requires: ["manage:system"])
|
||||||
usersTotal: Int
|
usersTotal: Int @auth(requires: ["manage:system", "manage:navigation", "manage:groups", "write:groups", "manage:users", "write:users"])
|
||||||
workingDirectory: String
|
workingDirectory: String @auth(requires: ["manage:system"])
|
||||||
}
|
}
|
||||||
|
@ -101,6 +101,10 @@ module.exports = class User extends Model {
|
|||||||
await this.generateHash()
|
await this.generateHash()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------
|
||||||
|
// Instance Methods
|
||||||
|
// ------------------------------------------------
|
||||||
|
|
||||||
async generateHash() {
|
async generateHash() {
|
||||||
if (this.password) {
|
if (this.password) {
|
||||||
if (bcryptRegexp.test(this.password)) { return }
|
if (bcryptRegexp.test(this.password)) { return }
|
||||||
@ -138,11 +142,18 @@ module.exports = class User extends Model {
|
|||||||
return (result && _.has(result, 'delta') && result.delta === 0)
|
return (result && _.has(result, 'delta') && result.delta === 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPermissions() {
|
getGlobalPermissions() {
|
||||||
const permissions = await this.$relatedQuery('groups').select('permissions').pluck('permissions')
|
return _.uniq(_.flatten(_.map(this.groups, 'permissions')))
|
||||||
this.permissions = _.uniq(_.flatten(permissions))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getGroups() {
|
||||||
|
return _.uniq(_.map(this.groups, 'id'))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------
|
||||||
|
// Model Methods
|
||||||
|
// ------------------------------------------------
|
||||||
|
|
||||||
static async processProfile(profile) {
|
static async processProfile(profile) {
|
||||||
let primaryEmail = ''
|
let primaryEmail = ''
|
||||||
if (_.isArray(profile.emails)) {
|
if (_.isArray(profile.emails)) {
|
||||||
@ -246,12 +257,17 @@ module.exports = class User extends Model {
|
|||||||
|
|
||||||
static async refreshToken(user) {
|
static async refreshToken(user) {
|
||||||
if (_.isSafeInteger(user)) {
|
if (_.isSafeInteger(user)) {
|
||||||
user = await WIKI.models.users.query().findById(user)
|
user = await WIKI.models.users.query().findById(user).eager('groups').modifyEager('groups', builder => {
|
||||||
|
builder.select('groups.id', 'permissions')
|
||||||
|
})
|
||||||
if (!user) {
|
if (!user) {
|
||||||
WIKI.logger.warn(`Failed to refresh token for user ${user}: Not found.`)
|
WIKI.logger.warn(`Failed to refresh token for user ${user}: Not found.`)
|
||||||
throw new WIKI.Error.AuthGenericError()
|
throw new WIKI.Error.AuthGenericError()
|
||||||
}
|
}
|
||||||
|
} else if(_.isNil(user.groups)) {
|
||||||
|
await user.$relatedQuery('groups').select('groups.id', 'permissions')
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
token: jwt.sign({
|
token: jwt.sign({
|
||||||
id: user.id,
|
id: user.id,
|
||||||
@ -261,7 +277,8 @@ module.exports = class User extends Model {
|
|||||||
timezone: user.timezone,
|
timezone: user.timezone,
|
||||||
localeCode: user.localeCode,
|
localeCode: user.localeCode,
|
||||||
defaultEditor: user.defaultEditor,
|
defaultEditor: user.defaultEditor,
|
||||||
permissions: ['manage:system']
|
permissions: user.getGlobalPermissions(),
|
||||||
|
groups: user.getGroups()
|
||||||
}, {
|
}, {
|
||||||
key: WIKI.config.certs.private,
|
key: WIKI.config.certs.private,
|
||||||
passphrase: WIKI.config.sessionSecret
|
passphrase: WIKI.config.sessionSecret
|
||||||
@ -398,8 +415,13 @@ module.exports = class User extends Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static async getGuestUser () {
|
static async getGuestUser () {
|
||||||
let user = await WIKI.models.users.query().findById(2)
|
const user = await WIKI.models.users.query().findById(2).eager('groups').modifyEager('groups', builder => {
|
||||||
user.getPermissions()
|
builder.select('groups.id', 'permissions')
|
||||||
|
})
|
||||||
|
if (!user) {
|
||||||
|
WIKI.logger.error('CRITICAL ERROR: Guest user is missing!')
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,9 +219,9 @@ module.exports = () => {
|
|||||||
})
|
})
|
||||||
const guestGroup = await WIKI.models.groups.query().insert({
|
const guestGroup = await WIKI.models.groups.query().insert({
|
||||||
name: 'Guests',
|
name: 'Guests',
|
||||||
permissions: JSON.stringify(['read:pages']),
|
permissions: JSON.stringify(['read:pages', 'read:assets', 'read:comments']),
|
||||||
pageRules: JSON.stringify([
|
pageRules: JSON.stringify([
|
||||||
{ id: 'guest', roles: ['READ', 'AS_READ', 'CM_READ'], match: 'START', deny: false, path: '', locales: [] }
|
{ id: 'guest', roles: ['read:pages', 'read:assets', 'read:comments'], match: 'START', deny: false, path: '', locales: [] }
|
||||||
]),
|
]),
|
||||||
isSystem: true
|
isSystem: true
|
||||||
})
|
})
|
||||||
|
13
server/views/notfound.pug
Normal file
13
server/views/notfound.pug
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
extends master.pug
|
||||||
|
|
||||||
|
block body
|
||||||
|
#root.is-fullscreen
|
||||||
|
v-app
|
||||||
|
.notfound
|
||||||
|
.notfound-content
|
||||||
|
img.animated.fadeIn(src='/svg/icon-delete-file.svg', alt='Not Found')
|
||||||
|
.headline= t('notfound.title')
|
||||||
|
.subheading.mt-3= t('notfound.subtitle')
|
||||||
|
v-btn.mt-5(color='red lighten-4', href='/', large, outline)
|
||||||
|
v-icon(left) home
|
||||||
|
span= t('notfound.gohome')
|
Loading…
x
Reference in New Issue
Block a user