浏览代码

feat: auth self-registration config + gql grouping

NGPixel 7 年之前
父节点
当前提交
0afa65fa58
共有 39 个文件被更改,包括 104 次插入50 次删除
  1. 29 8
      client/components/admin/admin-auth.vue
  2. 5 5
      client/components/admin/admin-groups-edit.vue
  3. 4 4
      client/components/admin/admin-groups.vue
  4. 3 3
      client/components/admin/admin-locale.vue
  5. 1 1
      client/components/admin/admin-system.vue
  6. 1 1
      client/components/admin/admin-theme.vue
  7. 1 1
      client/components/common/user-search.vue
  8. 3 3
      client/components/login.vue
  9. 5 4
      client/components/profile.vue
  10. 6 4
      client/components/profile/preferences.vue
  11. 12 4
      client/components/profile/profile.vue
  12. 0 0
      client/graph/admin/auth/auth-mutation-save-strategies.gql
  13. 4 1
      client/graph/admin/auth/auth-query-strategies.gql
  14. 0 0
      client/graph/admin/groups/groups-mutation-assign.gql
  15. 0 0
      client/graph/admin/groups/groups-mutation-create.gql
  16. 0 0
      client/graph/admin/groups/groups-mutation-delete.gql
  17. 0 0
      client/graph/admin/groups/groups-mutation-unassign.gql
  18. 0 0
      client/graph/admin/groups/groups-mutation-update.gql
  19. 0 0
      client/graph/admin/groups/groups-query-list.gql
  20. 0 0
      client/graph/admin/groups/groups-query-single.gql
  21. 0 0
      client/graph/admin/locale/locale-mutation-download.gql
  22. 0 0
      client/graph/admin/locale/locale-mutation-save.gql
  23. 0 0
      client/graph/admin/locale/locale-query-list.gql
  24. 0 0
      client/graph/admin/system/system-query-info.gql
  25. 0 0
      client/graph/admin/theme/theme-mutation-save.gql
  26. 0 0
      client/graph/admin/users/users-query-list.gql
  27. 0 0
      client/graph/common/common-locale-query.gql
  28. 0 0
      client/graph/common/common-users-query-search.gql
  29. 0 0
      client/graph/login/login-mutation-login.gql
  30. 0 0
      client/graph/login/login-mutation-tfa.gql
  31. 0 0
      client/graph/login/login-query-strategies.gql
  32. 1 1
      client/modules/localization.js
  33. 5 3
      server/core/auth.js
  34. 3 0
      server/db/migrations/2.0.0.js
  35. 15 4
      server/db/models/authentication.js
  36. 1 1
      server/graph/resolvers/authentication.js
  37. 3 0
      server/graph/schemas/authentication.graphql
  38. 1 1
      server/helpers/graph.js
  39. 1 1
      server/modules/authentication/google.js

+ 29 - 8
client/components/admin/admin-auth.vue

@@ -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: {

+ 5 - 5
client/components/admin/admin-groups-edit.vue

@@ -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: {

+ 4 - 4
client/components/admin/admin-groups.vue

@@ -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() {

+ 3 - 3
client/components/admin/admin-locale.vue

@@ -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() {

+ 1 - 1
client/components/admin/admin-system.vue

@@ -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: {

+ 1 - 1
client/components/admin/admin-theme.vue

@@ -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() {

+ 1 - 1
client/components/common/user-search.vue

@@ -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: {

+ 3 - 3
client/components/login.vue

@@ -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' },

+ 5 - 4
client/components/profile.vue

@@ -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
 }

+ 6 - 4
client/components/profile/preferences.vue

@@ -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>

+ 12 - 4
client/components/profile/profile.vue

@@ -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>

+ 0 - 0
client/graph/admin-auth-mutation-save-strategies.gql → client/graph/admin/auth/auth-mutation-save-strategies.gql


+ 4 - 1
client/graph/admin-auth-query-strategies.gql → client/graph/admin/auth/auth-query-strategies.gql

@@ -1,6 +1,6 @@
 query {
   authentication {
-    strategies {
+    strategies(orderBy: "title ASC") {
       isEnabled
       key
       props
@@ -10,6 +10,9 @@ query {
         key
         value
       }
+      selfRegistration
+      domainWhitelist
+      autoEnrollGroups
     }
   }
 }

+ 0 - 0
client/graph/admin-groups-mutation-assign.gql → client/graph/admin/groups/groups-mutation-assign.gql


+ 0 - 0
client/graph/admin-groups-mutation-create.gql → client/graph/admin/groups/groups-mutation-create.gql


+ 0 - 0
client/graph/admin-groups-mutation-delete.gql → client/graph/admin/groups/groups-mutation-delete.gql


+ 0 - 0
client/graph/admin-groups-mutation-unassign.gql → client/graph/admin/groups/groups-mutation-unassign.gql


+ 0 - 0
client/graph/admin-groups-mutation-update.gql → client/graph/admin/groups/groups-mutation-update.gql


+ 0 - 0
client/graph/admin-groups-query-list.gql → client/graph/admin/groups/groups-query-list.gql


+ 0 - 0
client/graph/admin-groups-query-single.gql → client/graph/admin/groups/groups-query-single.gql


+ 0 - 0
client/graph/admin-locale-mutation-download.gql → client/graph/admin/locale/locale-mutation-download.gql


+ 0 - 0
client/graph/admin-locale-mutation-save.gql → client/graph/admin/locale/locale-mutation-save.gql


+ 0 - 0
client/graph/admin-locale-query-list.gql → client/graph/admin/locale/locale-query-list.gql


+ 0 - 0
client/graph/admin-system-query-info.gql → client/graph/admin/system/system-query-info.gql


+ 0 - 0
client/graph/admin-theme-mutation-save.gql → client/graph/admin/theme/theme-mutation-save.gql


+ 0 - 0
client/graph/admin-users-query-list.gql → client/graph/admin/users/users-query-list.gql


+ 0 - 0
client/graph/common-locale-query.gql → client/graph/common/common-locale-query.gql


+ 0 - 0
client/graph/common-users-query-search.gql → client/graph/common/common-users-query-search.gql


+ 0 - 0
client/graph/login-mutation-login.gql → client/graph/login/login-mutation-login.gql


+ 0 - 0
client/graph/login-mutation-tfa.gql → client/graph/login/login-mutation-tfa.gql


+ 0 - 0
client/graph/login-query-strategies.gql → client/graph/login/login-query-strategies.gql


+ 1 - 1
client/modules/localization.js

@@ -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,

+ 5 - 3
server/core/auth.js

@@ -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)
 

+ 3 - 0
server/db/migrations/2.0.0.js

@@ -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 => {

+ 15 - 4
server/db/models/authentication.js

@@ -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: [] }
           })
         }
       })

+ 1 - 1
server/graph/resolvers/authentication.js

@@ -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) => {

+ 3 - 0
server/graph/schemas/authentication.graphql

@@ -56,6 +56,9 @@ type AuthenticationStrategy {
   useForm: Boolean!
   icon: String
   config: [KeyValuePair]
+  selfRegistration: Boolean!
+  domainWhitelist: [String]!
+  autoEnrollGroups: [String]!
 }
 
 type AuthenticationLoginResponse {

+ 1 - 1
server/helpers/graph.js

@@ -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])
   }
 }

+ 1 - 1
server/modules/authentication/google.js

@@ -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) {