feat: auth self-registration config + gql grouping
This commit is contained in:
parent
49834461a6
commit
0afa65fa58
@ -9,7 +9,8 @@
|
||||
|
||||
v-tab-item(key='settings', :transition='false', :reverse-transition='false')
|
||||
v-card.pa-3(flat, tile)
|
||||
v-subheader.pl-0.pb-2 Select which authentication strategies to enable:
|
||||
.body-2.grey--text.text--darken-1 Select which authentication strategies to enable:
|
||||
.caption.grey--text.pb-2 Some strategies require additional configuration in their dedicated tab (when selected).
|
||||
v-form
|
||||
v-checkbox(
|
||||
v-for='strategy in strategies',
|
||||
@ -27,19 +28,36 @@
|
||||
v-form
|
||||
v-subheader.pl-0 Strategy Configuration
|
||||
.body-1.ml-3(v-if='!strategy.config || strategy.config.length < 1') This strategy has no configuration options you can modify.
|
||||
v-text-field(v-else, v-for='cfg in strategy.config', :key='cfg.key', :label='cfg.key', prepend-icon='settings_applications')
|
||||
v-text-field(
|
||||
v-else
|
||||
v-for='cfg in strategy.config'
|
||||
:key='cfg.key'
|
||||
:label='cfg.key'
|
||||
v-model='cfg.value'
|
||||
prepend-icon='settings_applications'
|
||||
)
|
||||
v-divider
|
||||
v-subheader.pl-0 Registration
|
||||
v-switch.ml-3(
|
||||
v-model='auths',
|
||||
v-model='allowSelfRegistration',
|
||||
label='Allow self-registration',
|
||||
:value='true',
|
||||
color='primary',
|
||||
hint='Allow any user successfully authorized by the strategy to access the wiki.',
|
||||
persistent-hint
|
||||
)
|
||||
v-text-field.ml-3(label='Limit to specific email domains', prepend-icon='mail_outline')
|
||||
v-text-field.ml-3(label='Assign to group', prepend-icon='people')
|
||||
v-text-field.ml-3(
|
||||
label='Limit to specific email domains'
|
||||
prepend-icon='mail_outline'
|
||||
hint='Domain(s) seperated by comma. (e.g. domain1.com, domain2.com)'
|
||||
persistent-hint
|
||||
)
|
||||
v-text-field.ml-3(
|
||||
label='Assign to group'
|
||||
prepend-icon='people'
|
||||
hint='Automatically assign new users to these groups.'
|
||||
persistent-hint
|
||||
)
|
||||
|
||||
v-card-chin
|
||||
v-btn(color='primary')
|
||||
@ -54,14 +72,17 @@
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
|
||||
import strategiesQuery from 'gql/admin-auth-query-strategies.gql'
|
||||
import strategiesSaveMutation from 'gql/admin-auth-mutation-save-strategies.gql'
|
||||
import strategiesQuery from 'gql/admin/auth/auth-query-strategies.gql'
|
||||
import strategiesSaveMutation from 'gql/admin/auth/auth-mutation-save-strategies.gql'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
strategies: [],
|
||||
selectedStrategies: ['local']
|
||||
selectedStrategies: ['local'],
|
||||
selfRegistration: false,
|
||||
domainWhitelist: [],
|
||||
autoEnrollGroups: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -114,11 +114,11 @@
|
||||
import Criterias from '../common/criterias.vue'
|
||||
import UserSearch from '../common/user-search.vue'
|
||||
|
||||
import groupQuery from 'gql/admin-groups-query-single.gql'
|
||||
import assignUserMutation from 'gql/admin-groups-mutation-assign.gql'
|
||||
import deleteGroupMutation from 'gql/admin-groups-mutation-delete.gql'
|
||||
import unassignUserMutation from 'gql/admin-groups-mutation-unassign.gql'
|
||||
import updateGroupMutation from 'gql/admin-groups-mutation-update.gql'
|
||||
import groupQuery from 'gql/admin/groups/groups-query-single.gql'
|
||||
import assignUserMutation from 'gql/admin/groups/groups-mutation-assign.gql'
|
||||
import deleteGroupMutation from 'gql/admin/groups/groups-mutation-delete.gql'
|
||||
import unassignUserMutation from 'gql/admin/groups/groups-mutation-unassign.gql'
|
||||
import updateGroupMutation from 'gql/admin/groups/groups-mutation-update.gql'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -13,7 +13,7 @@
|
||||
.dialog-header.is-short New Group
|
||||
v-card-text
|
||||
v-text-field(v-model='newGroupName', label='Group Name', autofocus, counter='255', @keyup.enter='createGroup')
|
||||
v-card-actions
|
||||
v-card-chin
|
||||
v-spacer
|
||||
v-btn(flat, @click='newGroupDialog = false') Cancel
|
||||
v-btn(color='primary', @click='createGroup') Create
|
||||
@ -45,9 +45,9 @@
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
|
||||
import groupsQuery from 'gql/admin-groups-query-list.gql'
|
||||
import createGroupMutation from 'gql/admin-groups-mutation-create.gql'
|
||||
import deleteGroupMutation from 'gql/admin-groups-mutation-delete.gql'
|
||||
import groupsQuery from 'gql/admin/groups/groups-query-list.gql'
|
||||
import createGroupMutation from 'gql/admin/groups/groups-mutation-create.gql'
|
||||
import deleteGroupMutation from 'gql/admin/groups/groups-mutation-delete.gql'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
|
@ -128,9 +128,9 @@ import _ from 'lodash'
|
||||
|
||||
/* global WIKI */
|
||||
|
||||
import localesQuery from 'gql/admin-locale-query-list.gql'
|
||||
import localesDownloadMutation from 'gql/admin-locale-mutation-download.gql'
|
||||
import localesSaveMutation from 'gql/admin-locale-mutation-save.gql'
|
||||
import localesQuery from 'gql/admin/locale/locale-query-list.gql'
|
||||
import localesDownloadMutation from 'gql/admin/locale/locale-mutation-download.gql'
|
||||
import localesSaveMutation from 'gql/admin/locale/locale-mutation-save.gql'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
|
@ -106,7 +106,7 @@ import IconCube from 'mdi/cube'
|
||||
import IconDatabase from 'mdi/database'
|
||||
import IconNodeJs from 'mdi/nodejs'
|
||||
|
||||
import systemInfoQuery from 'gql/admin-system-query-info.gql'
|
||||
import systemInfoQuery from 'gql/admin/system/system-query-info.gql'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -50,7 +50,7 @@
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
|
||||
import themeSaveMutation from 'gql/admin-theme-mutation-save.gql'
|
||||
import themeSaveMutation from 'gql/admin/theme/theme-mutation-save.gql'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
|
@ -48,7 +48,7 @@
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
|
||||
import searchUsersQuery from 'gql/common-users-query-search.gql'
|
||||
import searchUsersQuery from 'gql/common/common-users-query-search.gql'
|
||||
|
||||
export default {
|
||||
filters: {
|
||||
|
@ -56,9 +56,9 @@
|
||||
import _ from 'lodash'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
import strategiesQuery from 'gql/login-query-strategies.gql'
|
||||
import loginMutation from 'gql/login-mutation-login.gql'
|
||||
import tfaMutation from 'gql/login-mutation-tfa.gql'
|
||||
import strategiesQuery from 'gql/login/login-query-strategies.gql'
|
||||
import loginMutation from 'gql/login/login-mutation-login.gql'
|
||||
import tfaMutation from 'gql/login/login-mutation-tfa.gql'
|
||||
|
||||
export default {
|
||||
i18nOptions: { namespaces: 'auth' },
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template lang='pug'>
|
||||
v-app.profile
|
||||
v-app(:dark='darkMode').profile
|
||||
nav-header
|
||||
v-navigation-drawer.pb-0(v-model='profileDrawerShown', app, fixed, clipped, left, permanent)
|
||||
v-list(dense)
|
||||
@ -22,7 +22,7 @@
|
||||
transition(name='profile-router')
|
||||
router-view
|
||||
|
||||
v-footer.py-2.justify-center(app, absolute, color='grey lighten-3', inset, height='auto')
|
||||
v-footer.py-2.justify-center(app, absolute, :color='darkMode ? "" : "grey lighten-3"', inset, height='auto')
|
||||
.caption.grey--text.text--darken-1 Powered by Wiki.js
|
||||
|
||||
v-snackbar(
|
||||
@ -41,7 +41,7 @@
|
||||
import VueRouter from 'vue-router'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
/* global WIKI */
|
||||
/* global WIKI, siteConfig */
|
||||
|
||||
const router = new VueRouter({
|
||||
mode: 'history',
|
||||
@ -75,7 +75,8 @@ export default {
|
||||
notificationState: {
|
||||
get() { return this.notification.isActive },
|
||||
set(newState) { this.$store.commit('updateNotificationState', newState) }
|
||||
}
|
||||
},
|
||||
darkMode() { return siteConfig.darkMode }
|
||||
},
|
||||
router
|
||||
}
|
||||
|
@ -18,8 +18,7 @@
|
||||
v-divider
|
||||
v-subheader.pl-0 Timezone
|
||||
v-select.grey.lighten-5(solo, flat)
|
||||
v-divider.my-0
|
||||
v-card-actions.grey.lighten-4
|
||||
v-card-chin
|
||||
v-spacer
|
||||
v-btn(color='primary')
|
||||
v-icon(left) chevron_right
|
||||
@ -32,8 +31,7 @@
|
||||
v-card-text
|
||||
v-subheader.pl-0 Default Editor
|
||||
v-select.grey.lighten-5(solo, flat)
|
||||
v-divider.my-0
|
||||
v-card-actions.grey.lighten-4
|
||||
v-card-chin
|
||||
v-spacer
|
||||
v-btn(color='primary')
|
||||
v-icon(left) chevron_right
|
||||
@ -42,10 +40,14 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* global siteConfig */
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return { }
|
||||
},
|
||||
computed: {
|
||||
darkMode() { return siteConfig.darkMode }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -17,8 +17,7 @@
|
||||
v-text-field(label='Name', :counter='255', v-model='name', prepend-icon='person')
|
||||
v-text-field(label='Job Title', :counter='255', prepend-icon='accessibility')
|
||||
v-text-field(label='Location / Office', :counter='255', prepend-icon='location_on')
|
||||
v-divider.my-0
|
||||
v-card-actions.grey.lighten-4
|
||||
v-card-chin
|
||||
v-spacer
|
||||
v-btn(color='primary')
|
||||
v-icon(left) chevron_right
|
||||
@ -29,8 +28,13 @@
|
||||
.subheading Authentication
|
||||
v-card-text
|
||||
v-subheader.pl-0 Provider
|
||||
v-toolbar(flat, color='purple lighten-5', dense).purple--text.text--darken-4
|
||||
v-icon(color='purple darken-4') supervised_user_circle
|
||||
v-toolbar(
|
||||
flat
|
||||
:color='darkMode ? "grey darken-2" : "purple lighten-5"'
|
||||
dense
|
||||
:class='darkMode ? "grey--text text--lighten-1" : "purple--text text--darken-4"'
|
||||
)
|
||||
v-icon(:color='darkMode ? "grey lighten-1" : "purple darken-4"') supervised_user_circle
|
||||
.subheading.ml-3 Local
|
||||
v-divider
|
||||
v-subheader.pl-0 Two-Factor Authentication (2FA)
|
||||
@ -72,12 +76,16 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* global siteConfig */
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
name: 'John Doe'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
darkMode() { return siteConfig.darkMode }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,6 +1,6 @@
|
||||
query {
|
||||
authentication {
|
||||
strategies {
|
||||
strategies(orderBy: "title ASC") {
|
||||
isEnabled
|
||||
key
|
||||
props
|
||||
@ -10,6 +10,9 @@ query {
|
||||
key
|
||||
value
|
||||
}
|
||||
selfRegistration
|
||||
domainWhitelist
|
||||
autoEnrollGroups
|
||||
}
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ import _ from 'lodash'
|
||||
|
||||
/* global siteConfig, graphQL */
|
||||
|
||||
import localeQuery from 'gql/common-locale-query.gql'
|
||||
import localeQuery from 'gql/common/common-locale-query.gql'
|
||||
|
||||
module.exports = {
|
||||
VueI18Next,
|
||||
|
@ -39,12 +39,14 @@ module.exports = {
|
||||
_.pull(currentStrategies, 'session')
|
||||
_.forEach(currentStrategies, stg => { passport.unuse(stg) })
|
||||
|
||||
// Load enable strategies
|
||||
const enabledStrategies = await WIKI.db.authentication.getEnabledStrategies()
|
||||
console.info(enabledStrategies)
|
||||
// Load enabled strategies
|
||||
const enabledStrategies = await WIKI.db.authentication.getStrategies()
|
||||
for (let idx in enabledStrategies) {
|
||||
const stg = enabledStrategies[idx]
|
||||
if (!stg.isEnabled) { continue }
|
||||
|
||||
const strategy = require(`../modules/authentication/${stg.key}`)
|
||||
|
||||
stg.config.callbackURL = `${WIKI.config.host}/login/${stg.key}/callback` // TODO: config.host
|
||||
strategy.init(passport, stg.config)
|
||||
|
||||
|
@ -31,6 +31,9 @@ exports.up = knex => {
|
||||
table.boolean('isEnabled').notNullable().defaultTo(false)
|
||||
table.boolean('useForm').notNullable().defaultTo(false)
|
||||
table.jsonb('config').notNullable()
|
||||
table.boolean('selfRegistration').notNullable().defaultTo(false)
|
||||
table.jsonb('domainWhitelist').notNullable()
|
||||
table.jsonb('autoEnrollGroups').notNullable()
|
||||
})
|
||||
// COMMENTS ----------------------------
|
||||
.createTable('comments', table => {
|
||||
|
@ -22,13 +22,21 @@ module.exports = class Authentication extends Model {
|
||||
title: {type: 'string'},
|
||||
isEnabled: {type: 'boolean'},
|
||||
useForm: {type: 'boolean'},
|
||||
config: {type: 'object'}
|
||||
config: {type: 'object'},
|
||||
selfRegistration: {type: 'boolean'},
|
||||
domainWhitelist: {type: 'object'},
|
||||
autoEnrollGroups: {type: 'object'}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static async getEnabledStrategies() {
|
||||
return WIKI.db.authentication.query().where({ isEnabled: true })
|
||||
static async getStrategies() {
|
||||
const strategies = await WIKI.db.authentication.query()
|
||||
return strategies.map(str => ({
|
||||
...str,
|
||||
domainWhitelist: _.get(str.domainWhitelist, 'v', []),
|
||||
autoEnrollGroups: _.get(str.autoEnrollGroups, 'v', [])
|
||||
}))
|
||||
}
|
||||
|
||||
static async refreshStrategiesFromDisk() {
|
||||
@ -46,7 +54,10 @@ module.exports = class Authentication extends Model {
|
||||
config: _.reduce(strategy.props, (result, value, key) => {
|
||||
_.set(result, value, '')
|
||||
return result
|
||||
}, {})
|
||||
}, {}),
|
||||
selfRegistration: false,
|
||||
domainWhitelist: { v: [] },
|
||||
autoEnrollGroups: { v: [] }
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -16,7 +16,7 @@ module.exports = {
|
||||
},
|
||||
AuthenticationQuery: {
|
||||
async strategies(obj, args, context, info) {
|
||||
let strategies = await WIKI.db.authentication.getEnabledStrategies()
|
||||
let strategies = await WIKI.db.authentication.getStrategies()
|
||||
strategies = strategies.map(stg => ({
|
||||
...stg,
|
||||
config: _.transform(stg.config, (res, value, key) => {
|
||||
|
@ -56,6 +56,9 @@ type AuthenticationStrategy {
|
||||
useForm: Boolean!
|
||||
icon: String
|
||||
config: [KeyValuePair]
|
||||
selfRegistration: Boolean!
|
||||
domainWhitelist: [String]!
|
||||
autoEnrollGroups: [String]!
|
||||
}
|
||||
|
||||
type AuthenticationLoginResponse {
|
||||
|
@ -24,7 +24,7 @@ module.exports = {
|
||||
return arr.filter(prvFilter.test)
|
||||
},
|
||||
orderBy (arr, orderString) {
|
||||
let orderParams = _.zip(orderString.split(',').map(ord => _.trim(ord).split(' ').map(_.trim)))
|
||||
let orderParams = _.zip(...orderString.split(',').map(ord => _.trim(ord).split(' ').map(_.trim)))
|
||||
return _.orderBy(arr, orderParams[0], orderParams[1])
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ const GoogleStrategy = require('passport-google-oauth20').Strategy
|
||||
|
||||
module.exports = {
|
||||
key: 'google',
|
||||
title: 'Google ID',
|
||||
title: 'Google',
|
||||
useForm: false,
|
||||
props: ['clientId', 'clientSecret'],
|
||||
init (passport, conf) {
|
||||
|
Loading…
Reference in New Issue
Block a user