feat: admin auth UI + fetch

pull/621/head
NGPixel 6 years ago
parent dd318eb2db
commit 5efbfc7370

@ -1,112 +1,103 @@
<template lang='pug'> <template lang='pug'>
v-card(flat) v-card(tile, color='grey lighten-5')
v-card(color='grey lighten-5') .pa-3.pt-4
.pa-3.pt-4 .headline.primary--text Authentication
.headline.primary--text Authentication .subheading.grey--text Configure the authentication settings of your wiki
.subheading.grey--text Configure the authentication settings of your wiki v-tabs(color='grey lighten-4', fixed-tabs, slider-color='primary', show-arrows)
v-tabs(color='grey lighten-4', fixed-tabs, slider-color='primary', show-arrows) v-tab(key='settings'): v-icon settings
v-tab(key='settings'): v-icon settings v-tab(v-for='strategy in activeStrategies', :key='strategy.key') {{ strategy.title }}
v-tab(v-for='provider in activeProviders', :key='provider.key') {{ provider.title }}
v-tab-item(key='settings', :transition='false', :reverse-transition='false') v-tab-item(key='settings', :transition='false', :reverse-transition='false')
v-card.pa-3 v-card.pa-3(flat, tile)
.body-2.pb-2 Select which authentication providers to enable: v-subheader.pl-0.pb-2 Select which authentication strategies to enable:
v-form v-form
v-checkbox( v-checkbox(
v-for='(provider, n) in providers', v-for='strategy in strategies',
v-model='auths', v-model='selectedStrategies',
:key='provider.key', :key='strategy.key',
:label='provider.title', :label='strategy.title',
:value='provider.key', :value='strategy.key',
color='primary', color='primary',
:disabled='provider.key === `local`' :disabled='strategy.key === `local`'
hide-details hide-details
) )
v-divider
v-btn(color='primary')
v-icon(left) chevron_right
| Set Providers
v-btn(color='black', dark)
v-icon(left) layers_clear
| Flush Sessions
v-btn(icon, @click='refresh')
v-icon.grey--text refresh
v-tab-item(v-for='(provider, n) in activeProviders', :key='provider.key', :transition='false', :reverse-transition='false') v-tab-item(v-for='(strategy, n) in activeStrategies', :key='strategy.key', :transition='false', :reverse-transition='false')
v-card.pa-3 v-card.pa-3(flat, tile)
v-form v-form
v-subheader Provider Configuration v-subheader.pl-0 Strategy Configuration
.body-1(v-if='!provider.props || provider.props.length < 1') This provider has no configuration options you can modify. .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='prop in provider.props', :key='prop', :label='prop', prepend-icon='mode_edit') v-text-field(v-else, v-for='cfg in strategy.config', :key='cfg.key', :label='cfg.key', prepend-icon='settings_applications')
v-divider v-divider
v-subheader Registration v-subheader.pl-0 Registration
v-switch.ml-3( v-switch.ml-3(
v-model='auths', v-model='auths',
label='Allow self-registration', label='Allow self-registration',
:value='true', :value='true',
color='primary', color='primary',
hint='Allow any user successfully authorized by the provider to access the wiki.', hint='Allow any user successfully authorized by the strategy to access the wiki.',
persistent-hint persistent-hint
) )
v-text-field(label='Limit to specific email domains', prepend-icon='mail_outline') v-text-field.ml-3(label='Limit to specific email domains', prepend-icon='mail_outline')
v-text-field(label='Assign to group', prepend-icon='people') v-text-field.ml-3(label='Assign to group', prepend-icon='people')
v-divider
v-btn(color='primary') v-divider.my-0
v-icon(left) chevron_right v-card-actions.grey.lighten-4
| Save Configuration v-btn(color='primary')
v-icon(left) chevron_right
span Save
v-spacer
v-btn(icon, @click='refresh')
v-icon.grey--text refresh
v-snackbar(
color='success'
top
v-model='refreshCompleted'
)
v-icon.mr-3(dark) cached
| List of providers has been refreshed.
</template> </template>
<script> <script>
import _ from 'lodash' import _ from 'lodash'
import gql from 'graphql-tag'
import strategiesQuery from 'gql/admin-auth-query-strategies.gql'
import strategiesSaveMutation from 'gql/admin-auth-mutation-save-strategies.gql'
export default { export default {
data() { data() {
return { return {
providers: [], strategies: [],
auths: ['local'], selectedStrategies: ['local']
refreshCompleted: false
} }
}, },
computed: { computed: {
activeProviders() { activeStrategies() {
return _.filter(this.providers, 'isEnabled') return _.filter(this.strategies, prv => _.includes(this.selectedStrategies, prv.key))
} }
}, },
apollo: { methods: {
providers: { async refresh() {
query: gql` await this.$apollo.queries.strategies.refetch()
query { this.$store.commit('showNotification', {
authentication { message: 'List of strategies has been refreshed.',
providers { style: 'success',
isEnabled icon: 'cached'
key })
props },
title async saveProviders() {
useForm this.$store.commit(`loadingStart`, 'admin-auth-savestrategies')
config { await this.$apollo.mutate({
key mutation: strategiesSaveMutation,
value variables: {
} strategies: this.auths
}
}
} }
`, })
update: (data) => data.authentication.providers this.$store.commit(`loadingStop`, 'admin-auth-savestrategies')
} }
}, },
methods: { apollo: {
async refresh() { strategies: {
await this.$apollo.queries.providers.refetch() query: strategiesQuery,
this.refreshCompleted = true fetchPolicy: 'network-only',
update: (data) => data.authentication.strategies,
watchLoading (isLoading) {
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-auth-refresh')
}
} }
} }
} }

@ -132,6 +132,7 @@ export default {
apollo: { apollo: {
info: { info: {
query: systemInfoQuery, query: systemInfoQuery,
fetchPolicy: 'network-only',
update: (data) => data.system.info, update: (data) => data.system.info,
watchLoading (isLoading) { watchLoading (isLoading) {
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-system-refresh') this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-system-refresh')

@ -1,8 +1,10 @@
<template lang="pug"> <template lang="pug">
v-app v-app
nav-header nav-header
.login(:class='{ "is-error": error }') .login
.login-container(:class='{ "is-expanded": strategies.length > 1, "is-loading": isLoading }') .login-container(:class='{ "is-expanded": strategies.length > 1, "is-loading": isLoading }')
.login-mascot
img(src='/svg/henry-reading.svg', alt='Henry')
.login-providers(v-show='strategies.length > 1') .login-providers(v-show='strategies.length > 1')
button(v-for='strategy in strategies', :class='{ "is-active": strategy.key === selectedStrategy }', @click='selectStrategy(strategy.key, strategy.useForm)', :title='strategy.title') button(v-for='strategy in strategies', :class='{ "is-active": strategy.key === selectedStrategy }', @click='selectStrategy(strategy.key, strategy.useForm)', :title='strategy.title')
em(v-html='strategy.icon') em(v-html='strategy.icon')
@ -160,7 +162,9 @@ export default {
style: 'success', style: 'success',
icon: 'check' icon: 'check'
}) })
window.location.replace('/') // TEMPORARY - USE RETURNURL _.delay(() => {
window.location.replace('/') // TEMPORARY - USE RETURNURL
}, 1000)
} }
this.isLoading = false this.isLoading = false
} else { } else {
@ -205,6 +209,9 @@ export default {
style: 'success', style: 'success',
icon: 'check' icon: 'check'
}) })
_.delay(() => {
window.location.replace('/') // TEMPORARY - USE RETURNURL
}, 1000)
this.isLoading = false this.isLoading = false
} else { } else {
throw new Error(respObj.responseResult.message) throw new Error(respObj.responseResult.message)
@ -275,6 +282,20 @@ export default {
height: 25vh; height: 25vh;
} }
&-mascot {
width: 200px;
height: 200px;
position: absolute;
top: -180px;
left: 50%;
margin-left: -100px;
z-index: 10;
@include until($tablet) {
display: none;
}
}
&-container { &-container {
position: relative; position: relative;
display: flex; display: flex;
@ -453,7 +474,7 @@ export default {
font-weight: 400; font-weight: 400;
color: mc('light-blue', '700'); color: mc('light-blue', '700');
text-shadow: 1px 1px 0 #FFF; text-shadow: 1px 1px 0 #FFF;
padding: 0; padding: 1rem 0 0 0;
margin: 0; margin: 0;
} }

@ -0,0 +1,12 @@
mutation($locale: String!, $autoUpdate: Boolean!, $namespacing: Boolean!, $namespaces: [String]!) {
localization {
updateLocale(locale: $locale, autoUpdate: $autoUpdate, namespacing: $namespacing, namespaces: $namespaces) {
responseResult {
succeeded
errorCode
slug
message
}
}
}
}

@ -0,0 +1,15 @@
query {
authentication {
strategies {
isEnabled
key
props
title
useForm
config {
key
value
}
}
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 63 KiB

@ -34,7 +34,7 @@
}, },
"homepage": "https://github.com/Requarks/wiki#readme", "homepage": "https://github.com/Requarks/wiki#readme",
"engines": { "engines": {
"node": ">=8.9.3" "node": ">=8.11"
}, },
"dependencies": { "dependencies": {
"apollo-server-express": "1.3.6", "apollo-server-express": "1.3.6",
@ -43,7 +43,7 @@
"bcryptjs-then": "1.0.1", "bcryptjs-then": "1.0.1",
"bluebird": "3.5.1", "bluebird": "3.5.1",
"body-parser": "1.18.3", "body-parser": "1.18.3",
"bugsnag": "2.3.1", "bugsnag": "2.4.0",
"bull": "3.4.2", "bull": "3.4.2",
"cheerio": "1.0.0-rc.2", "cheerio": "1.0.0-rc.2",
"child-process-promise": "2.2.1", "child-process-promise": "2.2.1",
@ -67,22 +67,22 @@
"getos": "3.1.0", "getos": "3.1.0",
"graphql": "0.13.2", "graphql": "0.13.2",
"graphql-list-fields": "2.0.2", "graphql-list-fields": "2.0.2",
"graphql-tools": "3.0.1", "graphql-tools": "3.0.2",
"i18next": "11.3.2", "i18next": "11.3.2",
"i18next-express-middleware": "1.1.1", "i18next-express-middleware": "1.1.1",
"i18next-localstorage-cache": "1.1.1", "i18next-localstorage-cache": "1.1.1",
"i18next-node-fs-backend": "1.0.0", "i18next-node-fs-backend": "1.0.0",
"image-size": "0.6.2", "image-size": "0.6.2",
"ioredis": "3.2.2", "ioredis": "3.2.2",
"js-yaml": "3.11.0", "js-yaml": "3.12.0",
"jsonwebtoken": "8.2.1", "jsonwebtoken": "8.2.2",
"klaw": "2.1.1", "klaw": "2.1.1",
"knex": "0.14.6", "knex": "0.14.6",
"lodash": "4.17.10", "lodash": "4.17.10",
"markdown-it": "8.4.1", "markdown-it": "8.4.1",
"markdown-it-abbr": "1.0.4", "markdown-it-abbr": "1.0.4",
"markdown-it-anchor": "4.0.0", "markdown-it-anchor": "4.0.0",
"markdown-it-attrs": "2.0.0", "markdown-it-attrs": "2.1.0",
"markdown-it-emoji": "1.4.0", "markdown-it-emoji": "1.4.0",
"markdown-it-expand-tabs": "1.0.13", "markdown-it-expand-tabs": "1.0.13",
"markdown-it-external-links": "0.0.6", "markdown-it-external-links": "0.0.6",
@ -95,7 +95,7 @@
"markdown-it-task-lists": "2.1.1", "markdown-it-task-lists": "2.1.1",
"mathjax-node": "2.1.0", "mathjax-node": "2.1.0",
"mime-types": "2.1.18", "mime-types": "2.1.18",
"moment": "2.22.1", "moment": "2.22.2",
"moment-timezone": "0.5.17", "moment-timezone": "0.5.17",
"mongodb": "3.1.0-beta4", "mongodb": "3.1.0-beta4",
"mssql": "4.1.0", "mssql": "4.1.0",
@ -103,7 +103,7 @@
"mysql2": "1.5.3", "mysql2": "1.5.3",
"node-2fa": "1.1.2", "node-2fa": "1.1.2",
"oauth2orize": "1.11.0", "oauth2orize": "1.11.0",
"objection": "1.1.8", "objection": "1.1.10",
"ora": "2.1.0", "ora": "2.1.0",
"passport": "0.4.0", "passport": "0.4.0",
"passport-auth0": "0.6.1", "passport-auth0": "0.6.1",
@ -126,8 +126,8 @@
"qr-image": "3.2.0", "qr-image": "3.2.0",
"raven": "2.6.2", "raven": "2.6.2",
"read-chunk": "2.1.0", "read-chunk": "2.1.0",
"remove-markdown": "0.2.2", "remove-markdown": "0.3.0",
"request": "2.86.0", "request": "2.87.0",
"request-promise": "4.2.2", "request-promise": "4.2.2",
"scim-query-filter-parser": "1.1.0", "scim-query-filter-parser": "1.1.0",
"semver": "5.5.0", "semver": "5.5.0",
@ -140,28 +140,31 @@
"yargs": "11.0.0" "yargs": "11.0.0"
}, },
"devDependencies": { "devDependencies": {
"@panter/vue-i18next": "0.9.1", "@panter/vue-i18next": "0.11.0",
"@vue/cli": "3.0.0-beta.10", "@vue/cli": "3.0.0-beta.15",
"apollo-client-preset": "1.0.8", "apollo-cache-inmemory": "1.2.2",
"apollo-client": "2.3.2",
"apollo-fetch": "0.7.0", "apollo-fetch": "0.7.0",
"apollo-link": "1.2.2",
"apollo-link-batch-http": "1.2.2", "apollo-link-batch-http": "1.2.2",
"autoprefixer": "8.5.0", "apollo-link-error": "1.0.9",
"apollo-link-http": "1.5.4",
"autoprefixer": "8.6.0",
"babel-cli": "6.26.0", "babel-cli": "6.26.0",
"babel-core": "6.26.3", "babel-core": "6.26.3",
"babel-eslint": "8.2.3", "babel-eslint": "8.2.3",
"babel-jest": "23.0.0-alpha.0", "babel-jest": "23.0.1",
"babel-loader": "7.1.4", "babel-loader": "7.1.4",
"babel-plugin-graphql-tag": "1.6.0", "babel-plugin-graphql-tag": "1.6.0",
"babel-plugin-lodash": "3.3.2", "babel-plugin-lodash": "3.3.2",
"babel-plugin-transform-imports": "1.5.0", "babel-plugin-transform-imports": "1.5.0",
"babel-polyfill": "6.26.0", "babel-polyfill": "6.26.0",
"babel-preset-env": "1.7.0", "babel-preset-env": "1.7.0",
"babel-preset-es2015": "6.24.1",
"babel-preset-stage-2": "6.24.1", "babel-preset-stage-2": "6.24.1",
"brace": "0.11.1", "brace": "0.11.1",
"cache-loader": "1.2.2", "cache-loader": "1.2.2",
"clean-webpack-plugin": "0.1.19", "clean-webpack-plugin": "0.1.19",
"colors": "1.2.5", "colors": "1.3.0",
"copy-webpack-plugin": "4.5.1", "copy-webpack-plugin": "4.5.1",
"css-loader": "0.28.11", "css-loader": "0.28.11",
"cssnano": "4.0.0-rc.2", "cssnano": "4.0.0-rc.2",
@ -171,7 +174,7 @@
"eslint-config-standard": "11.0.0", "eslint-config-standard": "11.0.0",
"eslint-plugin-import": "2.12.0", "eslint-plugin-import": "2.12.0",
"eslint-plugin-node": "6.0.1", "eslint-plugin-node": "6.0.1",
"eslint-plugin-promise": "3.7.0", "eslint-plugin-promise": "3.8.0",
"eslint-plugin-standard": "3.1.0", "eslint-plugin-standard": "3.1.0",
"eslint-plugin-vue": "4.5.0", "eslint-plugin-vue": "4.5.0",
"file-loader": "1.1.11", "file-loader": "1.1.11",
@ -183,27 +186,28 @@
"html-webpack-pug-plugin": "0.3.0", "html-webpack-pug-plugin": "0.3.0",
"i18next-xhr-backend": "1.5.1", "i18next-xhr-backend": "1.5.1",
"ignore-loader": "0.1.2", "ignore-loader": "0.1.2",
"jest": "22.4.4", "jest": "23.1.0",
"jest-junit": "4.0.0", "jest-junit": "5.0.0",
"js-cookie": "2.2.0", "js-cookie": "2.2.0",
"lodash-webpack-plugin": "0.11.5", "lodash-webpack-plugin": "0.11.5",
"mini-css-extract-plugin": "0.4.0", "mini-css-extract-plugin": "0.4.0",
"node-sass": "4.9.0", "node-sass": "4.9.0",
"offline-plugin": "5.0.3", "offline-plugin": "5.0.5",
"optimize-css-assets-webpack-plugin": "4.0.1", "optimize-css-assets-webpack-plugin": "4.0.2",
"postcss-cssnext": "3.1.0", "postcss-cssnext": "3.1.0",
"postcss-flexbugs-fixes": "3.3.1", "postcss-flexbugs-fixes": "3.3.1",
"postcss-flexibility": "2.0.0", "postcss-flexibility": "2.0.0",
"postcss-import": "11.1.0", "postcss-import": "11.1.0",
"postcss-loader": "2.1.5", "postcss-loader": "2.1.5",
"postcss-preset-env": "5.1.0",
"postcss-selector-parser": "5.0.0-rc.3", "postcss-selector-parser": "5.0.0-rc.3",
"pug-lint": "2.5.0", "pug-lint": "2.5.0",
"pug-loader": "2.4.0", "pug-loader": "2.4.0",
"pug-plain-loader": "1.0.0", "pug-plain-loader": "1.0.0",
"raw-loader": "0.5.1", "raw-loader": "0.5.1",
"react": "16.3.2", "react": "16.4.0",
"react-dom": "16.3.2", "react-dom": "16.4.0",
"sass-loader": "7.0.1", "sass-loader": "7.0.2",
"sass-resources-loader": "1.3.3", "sass-resources-loader": "1.3.3",
"script-ext-html-webpack-plugin": "2.0.1", "script-ext-html-webpack-plugin": "2.0.1",
"simple-progress-webpack-plugin": "1.1.2", "simple-progress-webpack-plugin": "1.1.2",
@ -211,15 +215,16 @@
"stylus": "0.54.5", "stylus": "0.54.5",
"stylus-loader": "3.0.2", "stylus-loader": "3.0.2",
"twemoji-awesome": "1.0.6", "twemoji-awesome": "1.0.6",
"vee-validate": "2.0.9", "url-loader": "1.0.1",
"vee-validate": "2.1.0-beta.1",
"velocity-animate": "1.5.1", "velocity-animate": "1.5.1",
"vue": "2.5.16", "vue": "2.5.16",
"vue-apollo": "3.0.0-beta.5", "vue-apollo": "3.0.0-beta.16",
"vue-clipboards": "1.2.4", "vue-clipboards": "1.2.4",
"vue-codemirror": "4.0.5", "vue-codemirror": "4.0.5",
"vue-hot-reload-api": "2.3.0", "vue-hot-reload-api": "2.3.0",
"vue-loader": "15.1.0", "vue-loader": "15.2.4",
"vue-material-design-icons": "1.4.0", "vue-material-design-icons": "1.5.1",
"vue-moment": "4.0.0-0", "vue-moment": "4.0.0-0",
"vue-router": "3.0.1", "vue-router": "3.0.1",
"vue-simple-breakpoints": "1.0.3", "vue-simple-breakpoints": "1.0.3",
@ -227,9 +232,9 @@
"vuetify": "1.0.18", "vuetify": "1.0.18",
"vuex": "3.0.1", "vuex": "3.0.1",
"vuex-persistedstate": "2.5.4", "vuex-persistedstate": "2.5.4",
"webpack": "4.8.3", "webpack": "4.10.2",
"webpack-bundle-analyzer": "2.12.0", "webpack-bundle-analyzer": "2.13.1",
"webpack-cli": "2.1.3", "webpack-cli": "3.0.1",
"webpack-dev-middleware": "3.1.3", "webpack-dev-middleware": "3.1.3",
"webpack-hot-middleware": "2.22.2", "webpack-hot-middleware": "2.22.2",
"webpack-merge": "4.1.2", "webpack-merge": "4.1.2",
@ -271,7 +276,8 @@
] ]
}, },
"postcss-flexbugs-fixes": {}, "postcss-flexbugs-fixes": {},
"postcss-flexibility": {} "postcss-flexibility": {},
"postcss-preset-env": {}
} }
}, },
"pugLintConfig": { "pugLintConfig": {
@ -290,24 +296,6 @@
"validateDivTags": true, "validateDivTags": true,
"validateIndentation": 2 "validateIndentation": 2
}, },
"nodemonConfig": {
"exec": "node server --dev",
"ignore": [
"assets/",
"client/",
"data/",
"dev/",
"test/",
"test-results/"
],
"ext": "js json graphql",
"watch": [
"server"
],
"env": {
"NODE_ENV": "development"
}
},
"collective": { "collective": {
"type": "opencollective", "type": "opencollective",
"url": "https://opencollective.com/wikijs", "url": "https://opencollective.com/wikijs",

@ -15,7 +15,7 @@ module.exports = {
async authentication() { return {} } async authentication() { return {} }
}, },
AuthenticationQuery: { AuthenticationQuery: {
async providers(obj, args, context, info) { async strategies(obj, args, context, info) {
let strategies = await WIKI.db.authentication.query().orderBy('title') let strategies = await WIKI.db.authentication.query().orderBy('title')
strategies = strategies.map(stg => ({ strategies = strategies.map(stg => ({
...stg, ...stg,
@ -52,7 +52,7 @@ module.exports = {
} }
} }
}, },
AuthenticationProvider: { AuthenticationStrategy: {
icon (ap, args) { icon (ap, args) {
return fs.readFile(path.join(WIKI.ROOTPATH, `assets/svg/auth-icon-${ap.key}.svg`), 'utf8').catch(err => { return fs.readFile(path.join(WIKI.ROOTPATH, `assets/svg/auth-icon-${ap.key}.svg`), 'utf8').catch(err => {
if (err.code === 'ENOENT') { if (err.code === 'ENOENT') {

@ -15,10 +15,10 @@ extend type Mutation {
# ----------------------------------------------- # -----------------------------------------------
type AuthenticationQuery { type AuthenticationQuery {
providers( strategies(
filter: String filter: String
orderBy: String orderBy: String
): [AuthenticationProvider] ): [AuthenticationStrategy]
} }
# ----------------------------------------------- # -----------------------------------------------
@ -37,7 +37,7 @@ type AuthenticationMutation {
securityCode: String! securityCode: String!
): DefaultResponse ): DefaultResponse
updateProvider( updateStrategy(
provider: String! provider: String!
isEnabled: Boolean! isEnabled: Boolean!
config: [KeyValuePairInput] config: [KeyValuePairInput]
@ -48,7 +48,7 @@ type AuthenticationMutation {
# TYPES # TYPES
# ----------------------------------------------- # -----------------------------------------------
type AuthenticationProvider { type AuthenticationStrategy {
isEnabled: Boolean! isEnabled: Boolean!
key: String! key: String!
props: [String] props: [String]

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save