feat: new login experience (#2139)
* feat: multiple auth instances * fix: auth setup + strategy initialization * feat: admin auth - add strategy * feat: redirect on login - group setting * feat: oauth2 generic - props definitions * feat: new login UI (wip) * feat: new login UI (wip) * feat: admin security login settings * feat: tabset editor indicators + print view improvements * fix: code styling
This commit is contained in:
@@ -129,7 +129,7 @@
|
||||
v-list-item-avatar(size='24', tile): v-icon mdi-heart-outline
|
||||
v-list-item-title {{ $t('admin:contribute.title') }}
|
||||
|
||||
v-content(:class='$vuetify.theme.dark ? "grey darken-5" : "grey lighten-5"')
|
||||
v-main(:class='$vuetify.theme.dark ? "grey darken-5" : "grey lighten-5"')
|
||||
transition(name='admin-router')
|
||||
router-view
|
||||
|
||||
|
@@ -18,195 +18,188 @@
|
||||
|
||||
v-flex(lg3, xs12)
|
||||
v-card.animated.fadeInUp
|
||||
v-toolbar(flat, color='primary', dark, dense)
|
||||
.subtitle-1 {{$t('admin:auth.strategies')}}
|
||||
v-toolbar(flat, color='teal', dark, dense)
|
||||
.subtitle-1 {{$t('admin:auth.activeStrategies')}}
|
||||
v-list(two-line, dense).py-0
|
||||
template(v-for='(str, idx) in strategies')
|
||||
v-list-item(:key='str.key', @click='selectedStrategy = str.key', :disabled='!str.isAvailable')
|
||||
v-list-item-avatar(size='24')
|
||||
v-icon(color='grey', v-if='!str.isAvailable') mdi-minus-box-outline
|
||||
v-icon(color='primary', v-else-if='str.isEnabled && str.key !== `local`', v-ripple, @click='str.isEnabled = false') mdi-checkbox-marked-outline
|
||||
v-icon(color='primary', v-else-if='str.isEnabled && str.key === `local`') mdi-checkbox-marked-outline
|
||||
v-icon(color='grey', v-else, v-ripple, @click='str.isEnabled = true') mdi-checkbox-blank-outline
|
||||
v-list-item-content
|
||||
v-list-item-title.body-2(:class='!str.isAvailable ? `grey--text` : (selectedStrategy === str.key ? `primary--text` : ``)') {{ str.title }}
|
||||
v-list-item-subtitle: .caption(:class='!str.isAvailable ? `grey--text text--lighten-1` : (selectedStrategy === str.key ? `blue--text ` : ``)') {{ str.description }}
|
||||
v-list-item-avatar(v-if='selectedStrategy === str.key', size='24')
|
||||
v-icon.animated.fadeInLeft(color='primary', large) mdi-chevron-right
|
||||
v-divider(v-if='idx < strategies.length - 1')
|
||||
|
||||
v-card.mt-3.animated.fadeInUp.wait-p2s
|
||||
v-toolbar(flat, color='primary', dark, dense)
|
||||
.subtitle-1 {{$t('admin:auth.globalAdvSettings')}}
|
||||
v-card-text
|
||||
v-text-field.md2(
|
||||
v-model='jwtAudience'
|
||||
outlined
|
||||
prepend-icon='mdi-account-group-outline'
|
||||
:label='$t(`admin:auth.jwtAudience`)'
|
||||
:hint='$t(`admin:auth.jwtAudienceHint`)'
|
||||
persistent-hint
|
||||
)
|
||||
v-text-field.mt-3.md2(
|
||||
v-model='jwtExpiration'
|
||||
outlined
|
||||
prepend-icon='mdi-clock-outline'
|
||||
:label='$t(`admin:auth.tokenExpiration`)'
|
||||
:hint='$t(`admin:auth.tokenExpirationHint`)'
|
||||
persistent-hint
|
||||
)
|
||||
v-text-field.mt-3.md2(
|
||||
v-model='jwtRenewablePeriod'
|
||||
outlined
|
||||
prepend-icon='mdi-update'
|
||||
:label='$t(`admin:auth.tokenRenewalPeriod`)'
|
||||
:hint='$t(`admin:auth.tokenRenewalPeriodHint`)'
|
||||
persistent-hint
|
||||
)
|
||||
draggable(
|
||||
v-model='activeStrategies'
|
||||
handle='.is-handle'
|
||||
direction='vertical'
|
||||
:store='order'
|
||||
)
|
||||
transition-group
|
||||
v-list-item(
|
||||
v-for='(str, idx) in activeStrategies'
|
||||
:key='str.key'
|
||||
@click='selectedStrategy = str.key'
|
||||
:class='selectedStrategy === str.key ? ($vuetify.theme.dark ? `grey darken-5` : `teal lighten-5`) : ``'
|
||||
)
|
||||
v-list-item-avatar.is-handle(size='24')
|
||||
v-icon(:color='selectedStrategy === str.key ? `teal` : `grey`') mdi-drag-horizontal
|
||||
v-list-item-content
|
||||
v-list-item-title.body-2(:class='selectedStrategy === str.key ? `teal--text` : ``') {{ str.displayName }}
|
||||
v-list-item-subtitle: .caption(:class='selectedStrategy === str.key ? `teal--text ` : ``') {{ str.strategy.title }}
|
||||
v-list-item-avatar(v-if='selectedStrategy === str.key', size='24')
|
||||
v-icon.animated.fadeInLeft(color='teal', large) mdi-chevron-right
|
||||
v-card-chin
|
||||
v-menu(offset-y, bottom, min-width='250px', max-width='550px', max-height='50vh', style='flex: 1 1;', center)
|
||||
template(v-slot:activator='{ on }')
|
||||
v-btn(v-on='on', color='primary', depressed, block)
|
||||
v-icon(left) mdi-plus
|
||||
span {{$t('admin:auth.addStrategy')}}
|
||||
v-list(dense)
|
||||
template(v-for='(str, idx) of strategies')
|
||||
v-list-item(
|
||||
:key='str.key'
|
||||
:disabled='str.isDisabled'
|
||||
@click='addStrategy(str)'
|
||||
)
|
||||
v-list-item-avatar(height='24', width='48', tile)
|
||||
v-img(:src='str.logo', width='48px', height='24px', contain, :style='str.isDisabled ? `opacity: .25;` : ``')
|
||||
v-list-item-content
|
||||
v-list-item-title {{str.title}}
|
||||
v-list-item-subtitle: .caption(:style='str.isDisabled ? `opacity: .4;` : ``') {{str.description}}
|
||||
v-divider(v-if='idx < strategies.length - 1')
|
||||
|
||||
v-flex(xs12, lg9)
|
||||
v-card.animated.fadeInUp.wait-p2s
|
||||
v-toolbar(color='primary', dense, flat, dark)
|
||||
.subtitle-1 {{strategy.title}}
|
||||
.subtitle-1 {{strategy.displayName}} #[em ({{strategy.strategy.title}})]
|
||||
v-spacer
|
||||
v-switch(
|
||||
dark
|
||||
color='blue lighten-5'
|
||||
label='Active'
|
||||
v-model='strategy.isEnabled'
|
||||
hide-details
|
||||
inset
|
||||
:disabled='strategy.key === `local`'
|
||||
)
|
||||
v-btn(small, outlined, dark, color='white', :disabled='strategy.key === `local`', @click='deleteStrategy()')
|
||||
v-icon(left) mdi-close
|
||||
span {{$t('common:actions.delete')}}
|
||||
v-card-info(color='blue')
|
||||
div
|
||||
span {{strategy.strategy.description}}
|
||||
.caption: a(:href='strategy.strategy.website') {{strategy.strategy.website}}
|
||||
v-spacer
|
||||
.authlogo
|
||||
img(:src='strategy.strategy.logo', :alt='strategy.strategy.title')
|
||||
v-card-text
|
||||
v-form
|
||||
.authlogo
|
||||
img(:src='strategy.logo', :alt='strategy.title')
|
||||
.body-2.pt-3 {{strategy.description}}
|
||||
.body-2.pt-3.pb-5: a(:href='strategy.website') {{strategy.website}}
|
||||
i18next.body-2(path='admin:auth.strategyState', tag='div', v-if='strategy.isEnabled')
|
||||
v-chip(color='green', small, dark, label, place='state') {{$t('admin:auth.strategyStateActive')}}
|
||||
span(v-if='selectedStrategy === `local`', place='locked') {{$t('admin:auth.strategyStateLocked')}}
|
||||
span(v-else, place='locked', v-text='')
|
||||
i18next.body-2(path='admin:auth.strategyState', tag='div', v-else)
|
||||
v-chip(color='red', small, dark, label, place='state') {{$t('admin:auth.strategyStateInactive')}}
|
||||
.overline.mb-5 {{$t('admin:auth.strategyConfiguration')}}
|
||||
v-text-field.mb-3(
|
||||
outlined
|
||||
label='Display Name'
|
||||
v-model='strategy.displayName'
|
||||
prepend-icon='mdi-format-title'
|
||||
hint='The title shown to the end user for this authentication strategy.'
|
||||
persistent-hint
|
||||
)
|
||||
template(v-for='cfg in strategy.config')
|
||||
v-select.mb-3(
|
||||
v-if='cfg.value.type === "string" && cfg.value.enum'
|
||||
outlined
|
||||
:items='cfg.value.enum'
|
||||
:key='cfg.key'
|
||||
:label='cfg.value.title'
|
||||
v-model='cfg.value.value'
|
||||
prepend-icon='mdi-cog-box'
|
||||
:hint='cfg.value.hint ? cfg.value.hint : ""'
|
||||
persistent-hint
|
||||
:class='cfg.value.hint ? "mb-2" : ""'
|
||||
:style='cfg.value.maxWidth > 0 ? `max-width:` + cfg.value.maxWidth + `px;` : ``'
|
||||
)
|
||||
v-switch.mb-6(
|
||||
v-else-if='cfg.value.type === "boolean"'
|
||||
:key='cfg.key'
|
||||
:label='cfg.value.title'
|
||||
v-model='cfg.value.value'
|
||||
color='primary'
|
||||
prepend-icon='mdi-cog-box'
|
||||
:hint='cfg.value.hint ? cfg.value.hint : ""'
|
||||
persistent-hint
|
||||
inset
|
||||
)
|
||||
v-textarea.mb-3(
|
||||
v-else-if='cfg.value.type === "string" && cfg.value.multiline'
|
||||
outlined
|
||||
:key='cfg.key'
|
||||
:label='cfg.value.title'
|
||||
v-model='cfg.value.value'
|
||||
prepend-icon='mdi-cog-box'
|
||||
:hint='cfg.value.hint ? cfg.value.hint : ""'
|
||||
persistent-hint
|
||||
:class='cfg.value.hint ? "mb-2" : ""'
|
||||
)
|
||||
v-text-field.mb-3(
|
||||
v-else
|
||||
outlined
|
||||
:key='cfg.key'
|
||||
:label='cfg.value.title'
|
||||
v-model='cfg.value.value'
|
||||
prepend-icon='mdi-cog-box'
|
||||
:hint='cfg.value.hint ? cfg.value.hint : ""'
|
||||
persistent-hint
|
||||
:class='cfg.value.hint ? "mb-2" : ""'
|
||||
:style='cfg.value.maxWidth > 0 ? `max-width:` + cfg.value.maxWidth + `px;` : ``'
|
||||
)
|
||||
v-divider.mt-3
|
||||
.overline.my-5 {{$t('admin:auth.registration')}}
|
||||
.pr-3
|
||||
v-switch.ml-3(
|
||||
v-model='strategy.selfRegistration'
|
||||
:label='$t(`admin:auth.selfRegistration`)'
|
||||
color='primary'
|
||||
:hint='$t(`admin:auth.selfRegistrationHint`)'
|
||||
persistent-hint
|
||||
inset
|
||||
)
|
||||
v-combobox.ml-3.mt-3(
|
||||
:label='$t(`admin:auth.domainsWhitelist`)'
|
||||
v-model='strategy.domainWhitelist'
|
||||
prepend-icon='mdi-email-check-outline'
|
||||
outlined
|
||||
:disabled='!strategy.selfRegistration'
|
||||
:hint='$t(`admin:auth.domainsWhitelistHint`)'
|
||||
persistent-hint
|
||||
small-chips
|
||||
deletable-chips
|
||||
clearable
|
||||
multiple
|
||||
chips
|
||||
)
|
||||
v-autocomplete.mt-3.ml-3(
|
||||
outlined
|
||||
:disabled='!strategy.selfRegistration'
|
||||
:items='groups'
|
||||
item-text='name'
|
||||
item-value='id'
|
||||
:label='$t(`admin:auth.autoEnrollGroups`)'
|
||||
v-model='strategy.autoEnrollGroups'
|
||||
prepend-icon='mdi-account-group'
|
||||
:hint='$t(`admin:auth.autoEnrollGroupsHint`)'
|
||||
small-chips
|
||||
persistent-hint
|
||||
deletable-chips
|
||||
clearable
|
||||
multiple
|
||||
chips
|
||||
)
|
||||
template(v-if='strategy.useForm')
|
||||
v-divider.mt-3
|
||||
.overline.my-5 {{$t('admin:auth.strategyConfiguration')}}
|
||||
.body-2.ml-3(v-if='!strategy.config || strategy.config.length < 1'): em {{$t('admin:auth.strategyNoConfiguration')}}
|
||||
template(v-else, v-for='cfg in strategy.config')
|
||||
v-select.mb-3(
|
||||
v-if='cfg.value.type === "string" && cfg.value.enum'
|
||||
outlined
|
||||
:items='cfg.value.enum'
|
||||
:key='cfg.key'
|
||||
:label='cfg.value.title'
|
||||
v-model='cfg.value.value'
|
||||
prepend-icon='mdi-cog-box'
|
||||
:hint='cfg.value.hint ? cfg.value.hint : ""'
|
||||
persistent-hint
|
||||
:class='cfg.value.hint ? "mb-2" : ""'
|
||||
:style='cfg.value.maxWidth > 0 ? `max-width:` + cfg.value.maxWidth + `px;` : ``'
|
||||
)
|
||||
v-switch.mb-6(
|
||||
v-else-if='cfg.value.type === "boolean"'
|
||||
:key='cfg.key'
|
||||
:label='cfg.value.title'
|
||||
v-model='cfg.value.value'
|
||||
color='primary'
|
||||
prepend-icon='mdi-cog-box'
|
||||
:hint='cfg.value.hint ? cfg.value.hint : ""'
|
||||
persistent-hint
|
||||
inset
|
||||
)
|
||||
v-textarea.mb-3(
|
||||
v-else-if='cfg.value.type === "string" && cfg.value.multiline'
|
||||
outlined
|
||||
:key='cfg.key'
|
||||
:label='cfg.value.title'
|
||||
v-model='cfg.value.value'
|
||||
prepend-icon='mdi-cog-box'
|
||||
:hint='cfg.value.hint ? cfg.value.hint : ""'
|
||||
persistent-hint
|
||||
:class='cfg.value.hint ? "mb-2" : ""'
|
||||
)
|
||||
v-text-field.mb-3(
|
||||
v-else
|
||||
outlined
|
||||
:key='cfg.key'
|
||||
:label='cfg.value.title'
|
||||
v-model='cfg.value.value'
|
||||
prepend-icon='mdi-cog-box'
|
||||
:hint='cfg.value.hint ? cfg.value.hint : ""'
|
||||
persistent-hint
|
||||
:class='cfg.value.hint ? "mb-2" : ""'
|
||||
:style='cfg.value.maxWidth > 0 ? `max-width:` + cfg.value.maxWidth + `px;` : ``'
|
||||
)
|
||||
v-divider.mt-3
|
||||
.overline.my-5 {{$t('admin:auth.registration')}}
|
||||
.pr-3
|
||||
v-switch.ml-3(
|
||||
v-model='strategy.selfRegistration'
|
||||
:label='$t(`admin:auth.selfRegistration`)'
|
||||
color='primary'
|
||||
:hint='$t(`admin:auth.selfRegistrationHint`)'
|
||||
persistent-hint
|
||||
inset
|
||||
)
|
||||
v-combobox.ml-3.mt-3(
|
||||
:label='$t(`admin:auth.domainsWhitelist`)'
|
||||
v-model='strategy.domainWhitelist'
|
||||
prepend-icon='mdi-email-check-outline'
|
||||
outlined
|
||||
:disabled='!strategy.selfRegistration'
|
||||
:hint='$t(`admin:auth.domainsWhitelistHint`)'
|
||||
persistent-hint
|
||||
small-chips
|
||||
deletable-chips
|
||||
clearable
|
||||
multiple
|
||||
chips
|
||||
)
|
||||
v-autocomplete.mt-3.ml-3(
|
||||
outlined
|
||||
:disabled='!strategy.selfRegistration'
|
||||
:items='groups'
|
||||
item-text='name'
|
||||
item-value='id'
|
||||
:label='$t(`admin:auth.autoEnrollGroups`)'
|
||||
v-model='strategy.autoEnrollGroups'
|
||||
prepend-icon='mdi-account-group'
|
||||
:hint='$t(`admin:auth.autoEnrollGroupsHint`)'
|
||||
small-chips
|
||||
persistent-hint
|
||||
deletable-chips
|
||||
clearable
|
||||
multiple
|
||||
chips
|
||||
)
|
||||
template(v-if='strategy.useForm')
|
||||
v-divider.mt-3
|
||||
.d-flex.my-5.align-center
|
||||
.overline {{$t('admin:auth.security')}}
|
||||
v-chip.ml-3.grey--text(outlined, small, label) Coming soon
|
||||
v-switch.ml-3(
|
||||
v-if='strategy.key === `local`'
|
||||
:disabled='!strategy.selfRegistration || true'
|
||||
v-model='strategy.recaptcha'
|
||||
label='Use reCAPTCHA by Google'
|
||||
color='primary'
|
||||
hint='Protects against spam robots and malicious registrations.'
|
||||
persistent-hint
|
||||
inset
|
||||
)
|
||||
v-switch.ml-3(
|
||||
v-model='strategy.recaptcha'
|
||||
:disabled='true'
|
||||
:label='$t(`admin:auth.force2fa`)'
|
||||
color='primary'
|
||||
:hint='$t(`admin:auth.force2faHint`)'
|
||||
persistent-hint
|
||||
inset
|
||||
)
|
||||
.d-flex.my-5.align-center
|
||||
.overline {{$t('admin:auth.security')}}
|
||||
v-chip.ml-3.grey--text(outlined, small, label) Coming soon
|
||||
v-switch.ml-3(
|
||||
v-if='strategy.key === `local`'
|
||||
:disabled='!strategy.selfRegistration || true'
|
||||
v-model='strategy.recaptcha'
|
||||
label='Use reCAPTCHA by Google'
|
||||
color='primary'
|
||||
hint='Protects against spam robots and malicious registrations.'
|
||||
persistent-hint
|
||||
inset
|
||||
)
|
||||
v-switch.ml-3(
|
||||
v-model='strategy.recaptcha'
|
||||
:disabled='true'
|
||||
:label='$t(`admin:auth.force2fa`)'
|
||||
color='primary'
|
||||
:hint='$t(`admin:auth.force2faHint`)'
|
||||
persistent-hint
|
||||
inset
|
||||
)
|
||||
|
||||
v-card.mt-4.wiki-form.animated.fadeInUp.wait-p4s(v-if='selectedStrategy !== `local`')
|
||||
v-toolbar(color='primary', dense, flat, dark)
|
||||
@@ -236,13 +229,18 @@
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
import gql from 'graphql-tag'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
|
||||
import groupsQuery from 'gql/admin/auth/auth-query-groups.gql'
|
||||
import strategiesQuery from 'gql/admin/auth/auth-query-strategies.gql'
|
||||
import strategiesSaveMutation from 'gql/admin/auth/auth-mutation-save-strategies.gql'
|
||||
import hostQuery from 'gql/admin/auth/auth-query-host.gql'
|
||||
|
||||
import draggable from 'vuedraggable'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
draggable
|
||||
},
|
||||
filters: {
|
||||
startCase(val) { return _.startCase(val) }
|
||||
},
|
||||
@@ -250,62 +248,107 @@ export default {
|
||||
return {
|
||||
groups: [],
|
||||
strategies: [],
|
||||
activeStrategies: [],
|
||||
selectedStrategy: '',
|
||||
host: '',
|
||||
strategy: {},
|
||||
jwtAudience: 'urn:wiki.js',
|
||||
jwtExpiration: '30m',
|
||||
jwtRenewablePeriod: '14d'
|
||||
strategy: {
|
||||
strategy: {}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
activeStrategies() {
|
||||
return _.filter(this.strategies, 'isEnabled')
|
||||
order: {
|
||||
get () {
|
||||
return this.strategies
|
||||
},
|
||||
set (val) {
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
selectedStrategy(newValue, oldValue) {
|
||||
this.strategy = _.find(this.strategies, ['key', newValue]) || {}
|
||||
this.strategy = _.find(this.activeStrategies, ['key', newValue]) || {}
|
||||
},
|
||||
strategies(newValue, oldValue) {
|
||||
activeStrategies(newValue, oldValue) {
|
||||
this.selectedStrategy = 'local'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async refresh() {
|
||||
await this.$apollo.queries.strategies.refetch()
|
||||
await this.$apollo.queries.activeStrategies.refetch()
|
||||
this.$store.commit('showNotification', {
|
||||
message: this.$t('admin:auth.refreshSuccess'),
|
||||
style: 'success',
|
||||
icon: 'cached'
|
||||
})
|
||||
},
|
||||
addStrategy (str) {
|
||||
const newStr = {
|
||||
key: uuid(),
|
||||
strategy: str,
|
||||
config: str.props.map(c => ({
|
||||
key: c.key,
|
||||
value: {
|
||||
...c,
|
||||
value: c.default
|
||||
}
|
||||
})),
|
||||
order: this.activeStrategies.length,
|
||||
displayName: str.title,
|
||||
selfRegistration: false,
|
||||
domainWhitelist: [],
|
||||
autoEnrollGroups: []
|
||||
}
|
||||
this.activeStrategies = [...this.activeStrategies, newStr]
|
||||
this.$nextTick(() => {
|
||||
this.selectedStrategy = newStr.key
|
||||
})
|
||||
},
|
||||
deleteStrategy () {
|
||||
this.activeStrategies = _.reject(this.activeStrategies, ['key', this.strategy.key])
|
||||
},
|
||||
async save() {
|
||||
this.$store.commit(`loadingStart`, 'admin-auth-savestrategies')
|
||||
try {
|
||||
await this.$apollo.mutate({
|
||||
mutation: strategiesSaveMutation,
|
||||
const resp = await this.$apollo.mutate({
|
||||
mutation: gql`
|
||||
mutation($strategies: [AuthenticationStrategyInput]!) {
|
||||
authentication {
|
||||
updateStrategies(strategies: $strategies) {
|
||||
responseResult {
|
||||
succeeded
|
||||
errorCode
|
||||
slug
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
config: {
|
||||
audience: this.jwtAudience,
|
||||
tokenExpiration: this.jwtExpiration,
|
||||
tokenRenewal: this.jwtRenewablePeriod
|
||||
},
|
||||
strategies: this.strategies.map(str => _.pick(str, [
|
||||
'isEnabled',
|
||||
'key',
|
||||
'config',
|
||||
'selfRegistration',
|
||||
'domainWhitelist',
|
||||
'autoEnrollGroups'
|
||||
])).map(str => ({...str, config: str.config.map(cfg => ({...cfg, value: JSON.stringify({ v: cfg.value.value })}))}))
|
||||
strategies: this.activeStrategies.map(str => ({
|
||||
key: str.key,
|
||||
strategyKey: str.strategy.key,
|
||||
displayName: str.displayName,
|
||||
order: str.order,
|
||||
config: str.config.map(cfg => ({...cfg, value: JSON.stringify({ v: cfg.value.value })})),
|
||||
selfRegistration: str.selfRegistration,
|
||||
domainWhitelist: str.domainWhitelist,
|
||||
autoEnrollGroups: str.autoEnrollGroups
|
||||
}))
|
||||
}
|
||||
})
|
||||
this.$store.commit('showNotification', {
|
||||
message: this.$t('admin:auth.saveSuccess'),
|
||||
style: 'success',
|
||||
icon: 'check'
|
||||
})
|
||||
if (_.get(resp, 'data.authentication.updateStrategies.responseResult.succeeded', false)) {
|
||||
this.$store.commit('showNotification', {
|
||||
message: this.$t('admin:auth.saveSuccess'),
|
||||
style: 'success',
|
||||
icon: 'check'
|
||||
})
|
||||
} else {
|
||||
throw new Error(_.get(resp, 'data.authentication.updateStrategies.responseResult.message', this.$t('common:error.unexpected')))
|
||||
}
|
||||
} catch (err) {
|
||||
this.$store.commit('pushGraphError', err)
|
||||
}
|
||||
@@ -314,9 +357,67 @@ export default {
|
||||
},
|
||||
apollo: {
|
||||
strategies: {
|
||||
query: strategiesQuery,
|
||||
query: gql`
|
||||
query {
|
||||
authentication {
|
||||
strategies {
|
||||
key
|
||||
title
|
||||
description
|
||||
isAvailable
|
||||
useForm
|
||||
logo
|
||||
website
|
||||
props {
|
||||
key
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
fetchPolicy: 'network-only',
|
||||
update: (data) => _.cloneDeep(data.authentication.strategies).map(str => ({
|
||||
update: (data) => _.get(data, 'authentication.strategies', []).map(str => ({
|
||||
...str,
|
||||
isDisabled: !str.isAvailable || str.key === `local`,
|
||||
props: _.sortBy(str.props.map(cfg => ({
|
||||
key: cfg.key,
|
||||
...JSON.parse(cfg.value)
|
||||
})), [t => t.order])
|
||||
})),
|
||||
watchLoading (isLoading) {
|
||||
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-auth-strategies-refresh')
|
||||
}
|
||||
},
|
||||
activeStrategies: {
|
||||
query: gql`
|
||||
query {
|
||||
authentication {
|
||||
activeStrategies {
|
||||
key
|
||||
strategy {
|
||||
key
|
||||
title
|
||||
description
|
||||
useForm
|
||||
logo
|
||||
website
|
||||
}
|
||||
config {
|
||||
key
|
||||
value
|
||||
}
|
||||
order
|
||||
displayName
|
||||
selfRegistration
|
||||
domainWhitelist
|
||||
autoEnrollGroups
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
fetchPolicy: 'network-only',
|
||||
update: (data) => _.get(data, 'authentication.activeStrategies', []).map(str => ({
|
||||
...str,
|
||||
config: _.sortBy(str.config.map(cfg => ({
|
||||
...cfg,
|
||||
@@ -324,7 +425,7 @@ export default {
|
||||
})), [t => t.value.order])
|
||||
})),
|
||||
watchLoading (isLoading) {
|
||||
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-auth-refresh')
|
||||
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-auth-activestrategies-refresh')
|
||||
}
|
||||
},
|
||||
groups: {
|
||||
@@ -351,7 +452,7 @@ export default {
|
||||
|
||||
.authlogo {
|
||||
width: 250px;
|
||||
height: 85px;
|
||||
height: 60px;
|
||||
float:right;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
@@ -1,23 +1,15 @@
|
||||
<template lang="pug">
|
||||
v-card(flat)
|
||||
v-card-text
|
||||
v-text-field(
|
||||
outlined
|
||||
v-model='group.name'
|
||||
label='Group Name'
|
||||
counter='255'
|
||||
prepend-icon='mdi-account-group'
|
||||
)
|
||||
v-alert.radius-7(
|
||||
v-if='group.isSystem'
|
||||
color='orange darken-2'
|
||||
:class='$vuetify.theme.dark ? "grey darken-4" : "orange lighten-5"'
|
||||
outlined
|
||||
:value='true'
|
||||
icon='mdi-lock-outline'
|
||||
) This is a system group. Some permissions cannot be modified.
|
||||
v-container.px-3.pb-3.pt-0(fluid, grid-list-md)
|
||||
v-container.px-3.pb-3.pt-3(fluid, grid-list-md)
|
||||
v-layout(row, wrap)
|
||||
v-flex(xs12, v-if='group.isSystem')
|
||||
v-alert.radius-7.mb-0(
|
||||
color='orange darken-2'
|
||||
:class='$vuetify.theme.dark ? "grey darken-4" : "orange lighten-5"'
|
||||
outlined
|
||||
:value='true'
|
||||
icon='mdi-lock-outline'
|
||||
) This is a system group. Some permissions cannot be modified.
|
||||
v-flex(xs12, md6, lg4, v-for='pmGroup in permissions', :key='pmGroup.category')
|
||||
v-card.md2(flat, :class='$vuetify.theme.dark ? "grey darken-3-d5" : "grey lighten-5"')
|
||||
.overline.px-5.pt-5.pb-3.grey--text.text--darken-2 {{pmGroup.category}}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<template lang="pug">
|
||||
v-card(flat)
|
||||
v-card-text(v-if='group.id === 1')
|
||||
v-alert.radius-7(
|
||||
v-alert.radius-7.mb-0(
|
||||
:class='$vuetify.theme.dark ? "grey darken-4" : "orange lighten-5"'
|
||||
color='orange darken-2'
|
||||
outlined
|
||||
|
@@ -12,7 +12,7 @@
|
||||
v-icon mdi-arrow-left
|
||||
v-dialog(v-model='deleteGroupDialog', max-width='500', v-if='!group.isSystem')
|
||||
template(v-slot:activator='{ on }')
|
||||
v-btn.ml-2(color='red', icon, outlined, v-on='on')
|
||||
v-btn.ml-3(color='red', icon, outlined, v-on='on')
|
||||
v-icon(color='red') mdi-trash-can-outline
|
||||
v-card
|
||||
.dialog-header.is-red Delete Group?
|
||||
@@ -21,11 +21,14 @@
|
||||
v-spacer
|
||||
v-btn(text, @click='deleteGroupDialog = false') Cancel
|
||||
v-btn(color='red', dark, @click='deleteGroup') Delete
|
||||
v-btn.ml-2(color='success', large, depressed, @click='updateGroup')
|
||||
v-btn.ml-3(color='success', large, depressed, @click='updateGroup')
|
||||
v-icon(left) mdi-check
|
||||
span Update Group
|
||||
v-card.mt-3
|
||||
v-tabs.grad-tabs(v-model='tab', :color='$vuetify.theme.dark ? `blue` : `primary`', fixed-tabs, show-arrows, icons-and-text)
|
||||
v-tab(key='settings')
|
||||
span Settings
|
||||
v-icon mdi-cog-box
|
||||
v-tab(key='permissions')
|
||||
span Permissions
|
||||
v-icon mdi-lock-pattern
|
||||
@@ -36,6 +39,44 @@
|
||||
span Users
|
||||
v-icon mdi-account-group
|
||||
|
||||
v-tab-item(key='settings', :transition='false', :reverse-transition='false')
|
||||
v-card(flat)
|
||||
template(v-if='group.id <= 2')
|
||||
v-card-text
|
||||
v-alert.radius-7.mb-0(
|
||||
color='orange darken-2'
|
||||
:class='$vuetify.theme.dark ? "grey darken-4" : "orange lighten-5"'
|
||||
outlined
|
||||
:value='true'
|
||||
icon='mdi-lock-outline'
|
||||
) This is a system group and its settings cannot be modified.
|
||||
v-divider
|
||||
v-card-text
|
||||
v-text-field(
|
||||
outlined
|
||||
v-model='group.name'
|
||||
label='Group Name'
|
||||
hide-details
|
||||
prepend-icon='mdi-account-group'
|
||||
style='max-width: 600px;'
|
||||
:disabled='group.id <= 2'
|
||||
)
|
||||
template(v-if='group.id > 2')
|
||||
v-divider
|
||||
v-card-text
|
||||
v-text-field(
|
||||
outlined
|
||||
v-model='group.redirectOnLogin'
|
||||
label='Redirect on Login'
|
||||
persistent-hint
|
||||
hint='The path / URL where the user will be redirected upon successful login.'
|
||||
prepend-icon='mdi-arrow-top-left-thick'
|
||||
append-icon='mdi-folder-search'
|
||||
@click:append='selectPage'
|
||||
style='max-width: 850px;'
|
||||
:counter='255'
|
||||
)
|
||||
|
||||
v-tab-item(key='permissions', :transition='false', :reverse-transition='false')
|
||||
group-permissions(v-model='group', @refresh='refresh')
|
||||
|
||||
@@ -44,21 +85,23 @@
|
||||
|
||||
v-tab-item(key='users', :transition='false', :reverse-transition='false')
|
||||
group-users(v-model='group', @refresh='refresh')
|
||||
|
||||
v-card-chin
|
||||
v-spacer
|
||||
.caption.grey--text.pr-2 Group ID #[strong {{group.id}}]
|
||||
|
||||
page-selector(mode='select', v-model='selectPageModal', :open-handler='selectPageHandle', path='home', :locale='currentLang')
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
import GroupPermissions from './admin-groups-edit-permissions.vue'
|
||||
import GroupRules from './admin-groups-edit-rules.vue'
|
||||
import GroupUsers from './admin-groups-edit-users.vue'
|
||||
|
||||
import groupQuery from 'gql/admin/groups/groups-query-single.gql'
|
||||
import deleteGroupMutation from 'gql/admin/groups/groups-mutation-delete.gql'
|
||||
import updateGroupMutation from 'gql/admin/groups/groups-mutation-update.gql'
|
||||
/* global siteConfig */
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -74,20 +117,55 @@ export default {
|
||||
isSystem: false,
|
||||
permissions: [],
|
||||
pageRules: [],
|
||||
users: []
|
||||
users: [],
|
||||
redirectOnLogin: '/'
|
||||
},
|
||||
deleteGroupDialog: false,
|
||||
tab: null
|
||||
tab: null,
|
||||
selectPageModal: false,
|
||||
currentLang: siteConfig.lang
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectPage () {
|
||||
this.selectPageModal = true
|
||||
},
|
||||
selectPageHandle ({ path, locale }) {
|
||||
this.group.redirectOnLogin = `/${locale}/${path}`
|
||||
},
|
||||
async updateGroup() {
|
||||
try {
|
||||
await this.$apollo.mutate({
|
||||
mutation: updateGroupMutation,
|
||||
mutation: gql`
|
||||
mutation (
|
||||
$id: Int!
|
||||
$name: String!
|
||||
$redirectOnLogin: String!
|
||||
$permissions: [String]!
|
||||
$pageRules: [PageRuleInput]!
|
||||
) {
|
||||
groups {
|
||||
update(
|
||||
id: $id
|
||||
name: $name
|
||||
redirectOnLogin: $redirectOnLogin
|
||||
permissions: $permissions
|
||||
pageRules: $pageRules
|
||||
) {
|
||||
responseResult {
|
||||
succeeded
|
||||
errorCode
|
||||
slug
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
id: this.group.id,
|
||||
name: this.group.name,
|
||||
redirectOnLogin: this.group.redirectOnLogin,
|
||||
permissions: this.group.permissions,
|
||||
pageRules: this.group.pageRules
|
||||
},
|
||||
@@ -108,7 +186,20 @@ export default {
|
||||
this.deleteGroupDialog = false
|
||||
try {
|
||||
await this.$apollo.mutate({
|
||||
mutation: deleteGroupMutation,
|
||||
mutation: gql`
|
||||
mutation ($id: Int!) {
|
||||
groups {
|
||||
delete(id: $id) {
|
||||
responseResult {
|
||||
succeeded
|
||||
errorCode
|
||||
slug
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
id: this.group.id
|
||||
},
|
||||
@@ -132,7 +223,34 @@ export default {
|
||||
},
|
||||
apollo: {
|
||||
group: {
|
||||
query: groupQuery,
|
||||
query: gql`
|
||||
query ($id: Int!) {
|
||||
groups {
|
||||
single(id: $id) {
|
||||
id
|
||||
name
|
||||
redirectOnLogin
|
||||
isSystem
|
||||
permissions
|
||||
pageRules {
|
||||
id
|
||||
path
|
||||
roles
|
||||
match
|
||||
deny
|
||||
locales
|
||||
}
|
||||
users {
|
||||
id
|
||||
name
|
||||
email
|
||||
}
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables() {
|
||||
return {
|
||||
id: _.toSafeInteger(this.$route.params.id)
|
||||
|
@@ -8,7 +8,9 @@
|
||||
.headline.blue--text.text--darken-2.animated.fadeInLeft Groups
|
||||
.subtitle-1.grey--text.animated.fadeInLeft.wait-p4s Manage groups and their permissions
|
||||
v-spacer
|
||||
v-btn.animated.fadeInDown.wait-p2s.mr-3(color='grey', outlined, @click='refresh', icon)
|
||||
v-btn.animated.fadeInDown.wait-p3s(icon, outlined, color='grey', href='https://docs.requarks.io/groups', target='_blank')
|
||||
v-icon mdi-help-circle
|
||||
v-btn.animated.fadeInDown.wait-p2s.mx-3(color='grey', outlined, @click='refresh', icon)
|
||||
v-icon mdi-refresh
|
||||
v-dialog(v-model='newGroupDialog', max-width='500')
|
||||
template(v-slot:activator='{ on }')
|
||||
|
@@ -93,25 +93,25 @@
|
||||
.caption Defines the duration for which the server should only deliver content through HTTPS.
|
||||
.caption It's a good idea to start with small values and make sure that nothing breaks on your wiki before moving to longer values.
|
||||
|
||||
v-divider.mt-3
|
||||
v-switch(
|
||||
inset
|
||||
label='Enforce CSP'
|
||||
color='red darken-2'
|
||||
v-model='config.securityCSP'
|
||||
persistent-hint
|
||||
hint='Restricts scripts to pre-approved content sources.'
|
||||
disabled
|
||||
)
|
||||
v-textarea.mt-5(
|
||||
label='CSP Directives'
|
||||
outlined
|
||||
v-model='config.securityCSPDirectives'
|
||||
prepend-icon='mdi-subdirectory-arrow-right'
|
||||
persistent-hint
|
||||
hint='One directive per line.'
|
||||
disabled
|
||||
)
|
||||
//- v-divider.mt-3
|
||||
//- v-switch(
|
||||
//- inset
|
||||
//- label='Enforce CSP'
|
||||
//- color='red darken-2'
|
||||
//- v-model='config.securityCSP'
|
||||
//- persistent-hint
|
||||
//- hint='Restricts scripts to pre-approved content sources.'
|
||||
//- disabled
|
||||
//- )
|
||||
//- v-textarea.mt-5(
|
||||
//- label='CSP Directives'
|
||||
//- outlined
|
||||
//- v-model='config.securityCSPDirectives'
|
||||
//- prepend-icon='mdi-subdirectory-arrow-right'
|
||||
//- persistent-hint
|
||||
//- hint='One directive per line.'
|
||||
//- disabled
|
||||
//- )
|
||||
|
||||
v-flex(lg6 xs12)
|
||||
v-card.animated.fadeInUp.wait-p2s
|
||||
@@ -142,6 +142,62 @@
|
||||
:suffix='$t(`admin:security.maxUploadBatchSuffix`)'
|
||||
style='max-width: 450px;'
|
||||
)
|
||||
|
||||
v-card.mt-3.animated.fadeInUp.wait-p2s
|
||||
v-toolbar(flat, color='primary', dark, dense)
|
||||
.subtitle-1 {{$t('admin:security.login')}}
|
||||
//- v-card-info(color='blue')
|
||||
//- span {{$t('admin:security.loginInfo')}}
|
||||
.overline.grey--text.pa-4 {{$t('admin:security.loginScreen')}}
|
||||
.px-4.pb-3
|
||||
v-text-field(
|
||||
outlined
|
||||
:label='$t(`admin:security.loginBgUrl`)'
|
||||
v-model='config.authLoginBgUrl'
|
||||
:hint='$t(`admin:security.loginBgUrlHint`)'
|
||||
persistent-hint
|
||||
prepend-icon='mdi-image-area'
|
||||
append-icon='mdi-folder-image'
|
||||
@click:append='browseLoginBg'
|
||||
)
|
||||
v-switch(
|
||||
inset
|
||||
:label='$t(`admin:security.bypassLogin`)'
|
||||
color='red darken-2'
|
||||
v-model='config.authAutoLogin'
|
||||
prepend-icon='mdi-fast-forward'
|
||||
persistent-hint
|
||||
:hint='$t(`admin:security.bypassLoginHint`)'
|
||||
)
|
||||
v-divider.mt-3
|
||||
.overline.grey--text.pa-4 {{$t('admin:security.jwt')}}
|
||||
.px-4.pb-3
|
||||
v-text-field(
|
||||
v-model='config.authJwtAudience'
|
||||
outlined
|
||||
prepend-icon='mdi-account-group-outline'
|
||||
:label='$t(`admin:auth.jwtAudience`)'
|
||||
:hint='$t(`admin:auth.jwtAudienceHint`)'
|
||||
persistent-hint
|
||||
)
|
||||
v-text-field.mt-3(
|
||||
v-model='config.authJwtExpiration'
|
||||
outlined
|
||||
prepend-icon='mdi-clock-outline'
|
||||
:label='$t(`admin:auth.tokenExpiration`)'
|
||||
:hint='$t(`admin:auth.tokenExpirationHint`)'
|
||||
persistent-hint
|
||||
)
|
||||
v-text-field.mt-3(
|
||||
v-model='config.authJwtRenewablePeriod'
|
||||
outlined
|
||||
prepend-icon='mdi-update'
|
||||
:label='$t(`admin:auth.tokenRenewalPeriod`)'
|
||||
:hint='$t(`admin:auth.tokenRenewalPeriodHint`)'
|
||||
persistent-hint
|
||||
)
|
||||
|
||||
component(:is='activeModal')
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -149,7 +205,17 @@ import _ from 'lodash'
|
||||
import { sync } from 'vuex-pathify'
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
import editorStore from '../../store/editor'
|
||||
|
||||
/* global WIKI */
|
||||
|
||||
WIKI.$store.registerModule('editor', editorStore)
|
||||
|
||||
export default {
|
||||
i18nOptions: { namespaces: 'editor' },
|
||||
components: {
|
||||
editorModalMedia: () => import(/* webpackChunkName: "editor", webpackMode: "lazy" */ '../editor/editor-modal-media.vue')
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
config: {
|
||||
@@ -163,7 +229,12 @@ export default {
|
||||
securityHSTS: false,
|
||||
securityHSTSDuration: 0,
|
||||
securityCSP: false,
|
||||
securityCSPDirectives: ''
|
||||
securityCSPDirectives: '',
|
||||
authAutoLogin: false,
|
||||
authLoginBgUrl: '',
|
||||
authJwtAudience: 'urn:wiki.js',
|
||||
authJwtExpiration: '30m',
|
||||
authJwtRenewablePeriod: '14d'
|
||||
},
|
||||
hstsDurations: [
|
||||
{ value: 300, text: '5 minutes' },
|
||||
@@ -184,6 +255,11 @@ export default {
|
||||
await this.$apollo.mutate({
|
||||
mutation: gql`
|
||||
mutation (
|
||||
$authAutoLogin: Boolean
|
||||
$authLoginBgUrl: String
|
||||
$authJwtAudience: String
|
||||
$authJwtExpiration: String
|
||||
$authJwtRenewablePeriod: String
|
||||
$uploadMaxFileSize: Int
|
||||
$uploadMaxFiles: Int
|
||||
$securityOpenRedirect: Boolean
|
||||
@@ -198,6 +274,11 @@ export default {
|
||||
) {
|
||||
site {
|
||||
updateConfig(
|
||||
authAutoLogin: $authAutoLogin,
|
||||
authLoginBgUrl: $authLoginBgUrl,
|
||||
authJwtAudience: $authJwtAudience,
|
||||
authJwtExpiration: $authJwtExpiration,
|
||||
authJwtRenewablePeriod: $authJwtRenewablePeriod,
|
||||
uploadMaxFileSize: $uploadMaxFileSize,
|
||||
uploadMaxFiles: $uploadMaxFiles,
|
||||
securityOpenRedirect: $securityOpenRedirect,
|
||||
@@ -221,6 +302,11 @@ export default {
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
authAutoLogin: _.get(this.config, 'authAutoLogin', false),
|
||||
authLoginBgUrl: _.get(this.config, 'authLoginBgUrl', ''),
|
||||
authJwtAudience: _.get(this.config, 'authJwtAudience', ''),
|
||||
authJwtExpiration: _.get(this.config, 'authJwtExpiration', ''),
|
||||
authJwtRenewablePeriod: _.get(this.config, 'authJwtRenewablePeriod', ''),
|
||||
uploadMaxFileSize: _.toSafeInteger(_.get(this.config, 'uploadMaxFileSize', 0)),
|
||||
uploadMaxFiles: _.toSafeInteger(_.get(this.config, 'uploadMaxFiles', 0)),
|
||||
securityOpenRedirect: _.get(this.config, 'securityOpenRedirect', false),
|
||||
@@ -245,14 +331,31 @@ export default {
|
||||
} catch (err) {
|
||||
this.$store.commit('pushGraphError', err)
|
||||
}
|
||||
},
|
||||
browseLoginBg () {
|
||||
this.$store.set('editor/editorKey', 'common')
|
||||
this.activeModal = 'editorModalMedia'
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.$root.$on('editorInsert', opts => {
|
||||
this.config.loginBgUrl = opts.path
|
||||
})
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$root.$off('editorInsert')
|
||||
},
|
||||
apollo: {
|
||||
config: {
|
||||
query: gql`
|
||||
{
|
||||
site {
|
||||
config {
|
||||
authAutoLogin
|
||||
authLoginBgUrl
|
||||
authJwtAudience
|
||||
authJwtExpiration
|
||||
authJwtRenewablePeriod
|
||||
uploadMaxFileSize
|
||||
uploadMaxFiles
|
||||
securityOpenRedirect
|
||||
|
@@ -11,7 +11,7 @@
|
||||
v-card-text.pt-5
|
||||
v-select(
|
||||
:items='providers'
|
||||
item-text='title'
|
||||
item-text='displayName'
|
||||
item-value='key'
|
||||
outlined
|
||||
prepend-icon='mdi-domain'
|
||||
@@ -230,19 +230,15 @@ export default {
|
||||
query: gql`
|
||||
query {
|
||||
authentication {
|
||||
strategies(
|
||||
isEnabled: true
|
||||
) {
|
||||
activeStrategies {
|
||||
key
|
||||
title
|
||||
icon
|
||||
color
|
||||
displayName
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
fetchPolicy: 'network-only',
|
||||
update: (data) => data.authentication.strategies,
|
||||
update: (data) => data.authentication.activeStrategies,
|
||||
watchLoading (isLoading) {
|
||||
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-users-strategies-refresh')
|
||||
}
|
||||
|
@@ -33,7 +33,7 @@
|
||||
label='Identity Provider'
|
||||
:items='strategies'
|
||||
v-model='filterStrategy'
|
||||
item-text='title'
|
||||
item-text='displayName'
|
||||
item-value='key'
|
||||
style='max-width: 300px;'
|
||||
dense
|
||||
@@ -162,13 +162,9 @@ export default {
|
||||
query: gql`
|
||||
query {
|
||||
authentication {
|
||||
strategies(
|
||||
isEnabled: true
|
||||
) {
|
||||
activeStrategies {
|
||||
key
|
||||
title
|
||||
icon
|
||||
color
|
||||
displayName
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -177,8 +173,8 @@ export default {
|
||||
update: (data) => {
|
||||
return _.concat({
|
||||
key: 'all',
|
||||
title: 'All Providers'
|
||||
}, data.authentication.strategies)
|
||||
displayName: 'All Providers'
|
||||
}, data.authentication.activeStrategies)
|
||||
},
|
||||
watchLoading (isLoading) {
|
||||
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-users-strategies-refresh')
|
||||
|
@@ -4,6 +4,7 @@
|
||||
top
|
||||
multi-line
|
||||
v-model='notificationState'
|
||||
:timeout='6000'
|
||||
)
|
||||
.text-left
|
||||
v-icon.mr-3(dark) mdi-{{ notification.icon }}
|
||||
@@ -26,16 +27,15 @@ export default {
|
||||
|
||||
<style lang='scss'>
|
||||
.nav-notify {
|
||||
// top: 60px;
|
||||
top: -64px;
|
||||
padding-top: 0;
|
||||
z-index: 999;
|
||||
|
||||
.v-snack__wrapper {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.v-snack__content {
|
||||
position: relative;
|
||||
margin-top: 0;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
|
@@ -245,6 +245,7 @@ import mermaid from 'mermaid'
|
||||
|
||||
// Helpers
|
||||
import katexHelper from './common/katex'
|
||||
import tabsetHelper from './markdown/tabset'
|
||||
|
||||
// ========================================
|
||||
// INIT
|
||||
@@ -433,6 +434,7 @@ export default {
|
||||
this.$store.set('editor/content', newContent)
|
||||
this.previewHTML = DOMPurify.sanitize(md.render(newContent))
|
||||
this.$nextTick(() => {
|
||||
tabsetHelper.format()
|
||||
this.renderMermaidDiagrams()
|
||||
Prism.highlightAllUnder(this.$refs.editorPreview)
|
||||
Array.from(this.$refs.editorPreview.querySelectorAll('pre.line-numbers')).forEach(pre => pre.classList.add('prismjs'))
|
||||
@@ -856,6 +858,44 @@ $editor-height-mobile: calc(100vh - 112px - 16px);
|
||||
p.line {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.tabset {
|
||||
background-color: mc('teal', '700');
|
||||
color: mc('teal', '100') !important;
|
||||
padding: 5px 12px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
border-radius: 5px 0 0 0;
|
||||
font-style: italic;
|
||||
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&-header {
|
||||
background-color: mc('teal', '500');
|
||||
color: #FFF !important;
|
||||
padding: 5px 12px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
margin-top: 0 !important;
|
||||
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-content {
|
||||
border-left: 5px solid mc('teal', '500');
|
||||
background-color: mc('teal', '50');
|
||||
padding: 0 15px 15px;
|
||||
overflow: hidden;
|
||||
|
||||
@at-root .theme--dark & {
|
||||
background-color: rgba(mc('teal', '500'), .1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
16
client/components/editor/markdown/tabset.js
Normal file
16
client/components/editor/markdown/tabset.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import cash from 'cash-dom'
|
||||
import _ from 'lodash'
|
||||
|
||||
export default {
|
||||
format () {
|
||||
for (let i = 1; i < 6; i++) {
|
||||
cash(`.editor-markdown-preview-content h${i}.tabset`).each((idx, elm) => {
|
||||
elm.innerHTML = 'Tabset ( rendered upon saving )'
|
||||
cash(elm).nextUntil(_.times(i, t => `h${t + 1}`).join(', '), `h${i + 1}`).each((hidx, hd) => {
|
||||
hd.classList.add('tabset-header')
|
||||
cash(hd).nextUntil(_.times(i + 1, t => `h${t + 1}`).join(', ')).wrapAll('<div class="tabset-content"></div>')
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,189 +1,240 @@
|
||||
<template lang="pug">
|
||||
v-app
|
||||
.login
|
||||
v-container(grid-list-lg)
|
||||
v-layout(row, wrap)
|
||||
v-flex(
|
||||
xs12
|
||||
offset-sm1, sm10
|
||||
offset-md2, md8
|
||||
offset-lg3, lg6
|
||||
offset-xl4, xl4
|
||||
.login-sd
|
||||
.d-flex
|
||||
.login-logo
|
||||
v-avatar(tile, size='34')
|
||||
v-img(:src='logoUrl')
|
||||
.login-title
|
||||
.text-h6 {{ siteTitle }}
|
||||
//-------------------------------------------------
|
||||
//- PROVIDERS LIST
|
||||
//-------------------------------------------------
|
||||
template(v-if='screen === `login` && strategies.length > 1')
|
||||
.login-subtitle.mt-5
|
||||
.text-subtitle-1 Select Authentication Provider
|
||||
.login-list
|
||||
v-list.elevation-1.radius-7(nav)
|
||||
v-list-item-group(v-model='selectedStrategyKey')
|
||||
v-list-item(
|
||||
v-for='(stg, idx) of strategies'
|
||||
:key='stg.key'
|
||||
:value='stg.key'
|
||||
:color='stg.strategy.color'
|
||||
)
|
||||
v-avatar.mr-3(tile, size='24', v-html='stg.strategy.icon')
|
||||
span.text-none {{stg.displayName}}
|
||||
//-------------------------------------------------
|
||||
//- LOGIN FORM
|
||||
//-------------------------------------------------
|
||||
template(v-if='screen === `login` && selectedStrategy.strategy.useForm')
|
||||
.login-subtitle
|
||||
.text-subtitle-1 Enter your credentials
|
||||
.login-form
|
||||
v-text-field(
|
||||
solo
|
||||
flat
|
||||
prepend-inner-icon='mdi-clipboard-account'
|
||||
background-color='white'
|
||||
hide-details
|
||||
ref='iptEmail'
|
||||
v-model='username'
|
||||
:placeholder='$t("auth:fields.emailUser")'
|
||||
)
|
||||
v-text-field.mt-2(
|
||||
solo
|
||||
flat
|
||||
prepend-inner-icon='mdi-form-textbox-password'
|
||||
background-color='white'
|
||||
hide-details
|
||||
ref='iptPassword'
|
||||
v-model='password'
|
||||
:append-icon='hidePassword ? "mdi-eye-off" : "mdi-eye"'
|
||||
@click:append='() => (hidePassword = !hidePassword)'
|
||||
:type='hidePassword ? "password" : "text"'
|
||||
:placeholder='$t("auth:fields.password")'
|
||||
@keyup.enter='login'
|
||||
)
|
||||
transition(name='fadeUp')
|
||||
v-card.elevation-5(v-show='isShown', light)
|
||||
v-toolbar(color='indigo', flat, dense, dark)
|
||||
v-spacer
|
||||
.subheading(v-if='screen === "tfa"') {{ $t('auth:tfa.subtitle') }}
|
||||
.subheading(v-if='screen === "changePwd"') {{ $t('auth:changePwd.subtitle') }}
|
||||
.subheading(v-else-if='selectedStrategy.key !== "local"') {{ $t('auth:loginUsingStrategy', { strategy: selectedStrategy.title, interpolation: { escapeValue: false } }) }}
|
||||
.subheading(v-else) {{ $t('auth:loginRequired') }}
|
||||
v-spacer
|
||||
v-card-text.text-center
|
||||
h1.display-1.indigo--text.py-2 {{ siteTitle }}
|
||||
template(v-if='screen === "login"')
|
||||
v-text-field.mt-3(
|
||||
solo
|
||||
flat
|
||||
prepend-icon='mdi-clipboard-account'
|
||||
background-color='grey lighten-4'
|
||||
hide-details
|
||||
ref='iptEmail'
|
||||
v-model='username'
|
||||
:placeholder='$t("auth:fields.emailUser")'
|
||||
)
|
||||
v-text-field.mt-2(
|
||||
solo
|
||||
flat
|
||||
prepend-icon='mdi-textbox-password'
|
||||
background-color='grey lighten-4'
|
||||
hide-details
|
||||
ref='iptPassword'
|
||||
v-model='password'
|
||||
:append-icon='hidePassword ? "mdi-eye-off" : "mdi-eye"'
|
||||
@click:append='() => (hidePassword = !hidePassword)'
|
||||
:type='hidePassword ? "password" : "text"'
|
||||
:placeholder='$t("auth:fields.password")'
|
||||
@keyup.enter='login'
|
||||
)
|
||||
template(v-else-if='screen === "tfa"')
|
||||
.body-2 Enter the security code generated from your trusted device:
|
||||
v-text-field.centered.mt-2(
|
||||
solo
|
||||
flat
|
||||
background-color='grey lighten-4'
|
||||
hide-details
|
||||
ref='iptTFA'
|
||||
v-model='securityCode'
|
||||
:placeholder='$t("auth:tfa.placeholder")'
|
||||
@keyup.enter='verifySecurityCode'
|
||||
)
|
||||
template(v-else-if='screen === "changePwd"')
|
||||
.body-2 {{$t('auth:changePwd.instructions')}}
|
||||
v-text-field.mt-2(
|
||||
type='password'
|
||||
solo
|
||||
flat
|
||||
background-color='grey lighten-4'
|
||||
hide-details
|
||||
ref='iptNewPassword'
|
||||
v-model='newPassword'
|
||||
:placeholder='$t(`auth:changePwd.newPasswordPlaceholder`)'
|
||||
)
|
||||
v-text-field.mt-2(
|
||||
type='password'
|
||||
solo
|
||||
flat
|
||||
background-color='grey lighten-4'
|
||||
hide-details
|
||||
v-model='newPasswordVerify'
|
||||
:placeholder='$t(`auth:changePwd.newPasswordVerifyPlaceholder`)'
|
||||
@keyup.enter='changePassword'
|
||||
)
|
||||
template(v-else-if='screen === "forgot"')
|
||||
.body-2 {{ $t('auth:forgotPasswordSubtitle') }}
|
||||
v-text-field.mt-3(
|
||||
solo
|
||||
flat
|
||||
prepend-icon='mdi-email'
|
||||
background-color='grey lighten-4'
|
||||
hide-details
|
||||
ref='iptEmailForgot'
|
||||
v-model='username'
|
||||
:placeholder='$t("auth:fields.email")'
|
||||
)
|
||||
v-card-actions.pb-4
|
||||
v-spacer
|
||||
v-btn(
|
||||
width='100%'
|
||||
max-width='250px'
|
||||
v-if='screen === "login"'
|
||||
large
|
||||
color='primary'
|
||||
dark
|
||||
@click='login'
|
||||
rounded
|
||||
:loading='isLoading'
|
||||
) {{ $t('auth:actions.login') }}
|
||||
v-btn(
|
||||
width='100%'
|
||||
max-width='250px'
|
||||
v-else-if='screen === "tfa"'
|
||||
large
|
||||
color='primary'
|
||||
dark
|
||||
@click='verifySecurityCode'
|
||||
rounded
|
||||
:loading='isLoading'
|
||||
) {{ $t('auth:tfa.verifyToken') }}
|
||||
v-btn(
|
||||
width='100%'
|
||||
max-width='250px'
|
||||
v-else-if='screen === "changePwd"'
|
||||
large
|
||||
color='primary'
|
||||
dark
|
||||
@click='changePassword'
|
||||
rounded
|
||||
:loading='isLoading'
|
||||
) {{ $t('auth:changePwd.proceed') }}
|
||||
v-btn(
|
||||
width='100%'
|
||||
max-width='250px'
|
||||
v-else-if='screen === "forgot"'
|
||||
large
|
||||
color='primary'
|
||||
dark
|
||||
@click='forgotPasswordSubmit'
|
||||
rounded
|
||||
:loading='isLoading'
|
||||
) {{ $t('auth:sendResetPassword') }}
|
||||
v-spacer
|
||||
v-card-actions.pb-3(v-if='screen === "login" && selectedStrategy.key === "local"')
|
||||
v-spacer
|
||||
a.caption(@click.stop.prevent='forgotPassword', href='#forgot') {{ $t('auth:forgotPasswordLink') }}
|
||||
v-spacer
|
||||
v-card-actions.pb-3(v-else-if='screen === "forgot"')
|
||||
v-spacer
|
||||
a.caption(@click.stop.prevent='screen = `login`', href='#cancelforgot') {{ $t('auth:forgotPasswordCancel') }}
|
||||
v-spacer
|
||||
template(v-if='screen === "login" && isSocialShown')
|
||||
v-divider
|
||||
v-card-text.grey.lighten-4.text-center
|
||||
.pb-2.body-2.text-xs-center.grey--text.text--darken-2 {{ $t('auth:orLoginUsingStrategy') }}
|
||||
v-btn.mx-1.social-login-btn(
|
||||
v-for='strategy in strategies', :key='strategy.key'
|
||||
large
|
||||
@click='selectStrategy(strategy)'
|
||||
dark
|
||||
:color='strategy.color'
|
||||
:depressed='strategy.key === selectedStrategy.key'
|
||||
)
|
||||
v-avatar.mr-3(tile, :class='strategy.color', size='24', v-html='strategy.icon')
|
||||
span(style='text-transform: none;') {{ strategy.title }}
|
||||
template(v-if='screen === "login" && selectedStrategy.key === `local` && selectedStrategy.selfRegistration')
|
||||
v-divider
|
||||
v-card-actions.py-3(:class='isSocialShown ? "" : "grey lighten-4"')
|
||||
v-spacer
|
||||
i18next.caption(path='auth:switchToRegister.text', tag='div')
|
||||
a.caption(href='/register', place='link') {{ $t('auth:switchToRegister.link') }}
|
||||
v-spacer
|
||||
v-btn.mt-2.text-none(
|
||||
width='100%'
|
||||
v-if='screen === "login"'
|
||||
large
|
||||
color='primary'
|
||||
dark
|
||||
@click='login'
|
||||
:loading='isLoading'
|
||||
) {{ $t('auth:actions.login') }}
|
||||
.text-center.mt-5(v-if='screen === "login"')
|
||||
v-btn.text-none(
|
||||
text
|
||||
rounded
|
||||
color='grey darken-3'
|
||||
@click.stop.prevent='forgotPassword'
|
||||
href='#forgot'
|
||||
): .caption {{ $t('auth:forgotPasswordLink') }}
|
||||
v-btn.text-none(
|
||||
v-if='screen === "login" && selectedStrategyKey === `local` && selectedStrategy.selfRegistration'
|
||||
color='indigo darken-2'
|
||||
text
|
||||
rounded
|
||||
href='/register'
|
||||
): .caption {{ $t('auth:switchToRegister.link') }}
|
||||
//- .login-main
|
||||
//- v-container(grid-list-lg, fluid)
|
||||
//- v-row(no-gutters)
|
||||
//- v-col(cols='12', xl='4')
|
||||
//- transition(name='fadeUp')
|
||||
//- v-card.elevation-5(v-show='isShown', light)
|
||||
//- v-toolbar(color='indigo', flat, dense, dark)
|
||||
//- v-spacer
|
||||
//- .subheading(v-if='screen === "tfa"') {{ $t('auth:tfa.subtitle') }}
|
||||
//- .subheading(v-if='screen === "changePwd"') {{ $t('auth:changePwd.subtitle') }}
|
||||
//- .subheading(v-else-if='selectedStrategy.key !== "local"') {{ $t('auth:loginUsingStrategy', { strategy: selectedStrategy.title, interpolation: { escapeValue: false } }) }}
|
||||
//- .subheading(v-else) {{ $t('auth:loginRequired') }}
|
||||
//- v-spacer
|
||||
//- v-card-text.text-center
|
||||
//- h1.display-1.indigo--text.py-2 {{ siteTitle }}
|
||||
//- template(v-if='screen === "login"')
|
||||
//- v-text-field.mt-3(
|
||||
//- solo
|
||||
//- flat
|
||||
//- prepend-icon='mdi-clipboard-account'
|
||||
//- background-color='grey lighten-4'
|
||||
//- hide-details
|
||||
//- ref='iptEmail'
|
||||
//- v-model='username'
|
||||
//- :placeholder='$t("auth:fields.emailUser")'
|
||||
//- )
|
||||
//- v-text-field.mt-2(
|
||||
//- solo
|
||||
//- flat
|
||||
//- prepend-icon='mdi-textbox-password'
|
||||
//- background-color='grey lighten-4'
|
||||
//- hide-details
|
||||
//- ref='iptPassword'
|
||||
//- v-model='password'
|
||||
//- :append-icon='hidePassword ? "mdi-eye-off" : "mdi-eye"'
|
||||
//- @click:append='() => (hidePassword = !hidePassword)'
|
||||
//- :type='hidePassword ? "password" : "text"'
|
||||
//- :placeholder='$t("auth:fields.password")'
|
||||
//- @keyup.enter='login'
|
||||
//- )
|
||||
//- template(v-else-if='screen === "tfa"')
|
||||
//- .body-2 Enter the security code generated from your trusted device:
|
||||
//- v-text-field.centered.mt-2(
|
||||
//- solo
|
||||
//- flat
|
||||
//- background-color='grey lighten-4'
|
||||
//- hide-details
|
||||
//- ref='iptTFA'
|
||||
//- v-model='securityCode'
|
||||
//- :placeholder='$t("auth:tfa.placeholder")'
|
||||
//- @keyup.enter='verifySecurityCode'
|
||||
//- )
|
||||
//- template(v-else-if='screen === "changePwd"')
|
||||
//- .body-2 {{$t('auth:changePwd.instructions')}}
|
||||
//- v-text-field.mt-2(
|
||||
//- type='password'
|
||||
//- solo
|
||||
//- flat
|
||||
//- background-color='grey lighten-4'
|
||||
//- hide-details
|
||||
//- ref='iptNewPassword'
|
||||
//- v-model='newPassword'
|
||||
//- :placeholder='$t(`auth:changePwd.newPasswordPlaceholder`)'
|
||||
//- )
|
||||
//- v-text-field.mt-2(
|
||||
//- type='password'
|
||||
//- solo
|
||||
//- flat
|
||||
//- background-color='grey lighten-4'
|
||||
//- hide-details
|
||||
//- v-model='newPasswordVerify'
|
||||
//- :placeholder='$t(`auth:changePwd.newPasswordVerifyPlaceholder`)'
|
||||
//- @keyup.enter='changePassword'
|
||||
//- )
|
||||
//- template(v-else-if='screen === "forgot"')
|
||||
//- .body-2 {{ $t('auth:forgotPasswordSubtitle') }}
|
||||
//- v-text-field.mt-3(
|
||||
//- solo
|
||||
//- flat
|
||||
//- prepend-icon='mdi-email'
|
||||
//- background-color='grey lighten-4'
|
||||
//- hide-details
|
||||
//- ref='iptEmailForgot'
|
||||
//- v-model='username'
|
||||
//- :placeholder='$t("auth:fields.email")'
|
||||
//- )
|
||||
//- v-card-actions.pb-4
|
||||
//- v-spacer
|
||||
//- v-btn(
|
||||
//- width='100%'
|
||||
//- max-width='250px'
|
||||
//- v-if='screen === "login"'
|
||||
//- large
|
||||
//- color='primary'
|
||||
//- dark
|
||||
//- @click='login'
|
||||
//- rounded
|
||||
//- :loading='isLoading'
|
||||
//- ) {{ $t('auth:actions.login') }}
|
||||
//- v-btn(
|
||||
//- width='100%'
|
||||
//- max-width='250px'
|
||||
//- v-else-if='screen === "tfa"'
|
||||
//- large
|
||||
//- color='primary'
|
||||
//- dark
|
||||
//- @click='verifySecurityCode'
|
||||
//- rounded
|
||||
//- :loading='isLoading'
|
||||
//- ) {{ $t('auth:tfa.verifyToken') }}
|
||||
//- v-btn(
|
||||
//- width='100%'
|
||||
//- max-width='250px'
|
||||
//- v-else-if='screen === "changePwd"'
|
||||
//- large
|
||||
//- color='primary'
|
||||
//- dark
|
||||
//- @click='changePassword'
|
||||
//- rounded
|
||||
//- :loading='isLoading'
|
||||
//- ) {{ $t('auth:changePwd.proceed') }}
|
||||
//- v-btn(
|
||||
//- width='100%'
|
||||
//- max-width='250px'
|
||||
//- v-else-if='screen === "forgot"'
|
||||
//- large
|
||||
//- color='primary'
|
||||
//- dark
|
||||
//- @click='forgotPasswordSubmit'
|
||||
//- rounded
|
||||
//- :loading='isLoading'
|
||||
//- ) {{ $t('auth:sendResetPassword') }}
|
||||
//- v-spacer
|
||||
//- v-card-actions.pb-3(v-if='screen === "login" && selectedStrategy.key === "local"')
|
||||
//- v-spacer
|
||||
//- a.caption(@click.stop.prevent='forgotPassword', href='#forgot') {{ $t('auth:forgotPasswordLink') }}
|
||||
//- v-spacer
|
||||
//- v-card-actions.pb-3(v-else-if='screen === "forgot"')
|
||||
//- v-spacer
|
||||
//- a.caption(@click.stop.prevent='screen = `login`', href='#cancelforgot') {{ $t('auth:forgotPasswordCancel') }}
|
||||
//- v-spacer
|
||||
|
||||
loader(v-model='isLoading', :color='loaderColor', :title='loaderTitle', :subtitle='$t(`auth:pleaseWait`)')
|
||||
nav-footer(color='grey darken-5', dark-color='grey darken-5')
|
||||
notify
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/* global siteConfig */
|
||||
|
||||
// <span>Photo by <a href="https://unsplash.com/@isaacquesada?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Isaac Quesada</a> on <a href="/t/textures-patterns?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></span>
|
||||
|
||||
import _ from 'lodash'
|
||||
import Cookies from 'js-cookie'
|
||||
|
||||
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'
|
||||
import changePasswordMutation from 'gql/login/login-mutation-changepassword.gql'
|
||||
import gql from 'graphql-tag'
|
||||
import { sync } from 'vuex-pathify'
|
||||
|
||||
export default {
|
||||
i18nOptions: { namespaces: 'auth' },
|
||||
@@ -191,7 +242,8 @@ export default {
|
||||
return {
|
||||
error: false,
|
||||
strategies: [],
|
||||
selectedStrategy: { key: 'local' },
|
||||
selectedStrategyKey: 'local',
|
||||
selectedStrategy: { key: 'local', strategy: { useForm: true } },
|
||||
screen: 'login',
|
||||
username: '',
|
||||
password: '',
|
||||
@@ -207,40 +259,39 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
activeModal: sync('editor/activeModal'),
|
||||
siteTitle () {
|
||||
return siteConfig.title
|
||||
},
|
||||
isSocialShown () {
|
||||
return this.strategies.length > 1
|
||||
}
|
||||
},
|
||||
logoUrl () { return siteConfig.logoUrl }
|
||||
},
|
||||
watch: {
|
||||
strategies(newValue, oldValue) {
|
||||
this.selectedStrategy = _.find(newValue, ['key', 'local'])
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.isShown = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.iptEmail.focus()
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* SELECT STRATEGY
|
||||
*/
|
||||
selectStrategy (strategy) {
|
||||
this.selectedStrategy = strategy
|
||||
this.selectedStrategy = _.head(newValue)
|
||||
},
|
||||
selectedStrategyKey (newValue, oldValue) {
|
||||
this.selectedStrategy = _.find(this.strategies, ['key', newValue])
|
||||
this.screen = 'login'
|
||||
if (!strategy.useForm) {
|
||||
if (!this.selectedStrategy.strategy.useForm) {
|
||||
this.isLoading = true
|
||||
window.location.assign('/login/' + strategy.key)
|
||||
window.location.assign('/login/' + newValue)
|
||||
} else {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.iptEmail.focus()
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.isShown = true
|
||||
this.$nextTick(() => {
|
||||
// this.$refs.iptEmail.focus()
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* LOGIN
|
||||
*/
|
||||
@@ -265,7 +316,24 @@ export default {
|
||||
this.isLoading = true
|
||||
try {
|
||||
let resp = await this.$apollo.mutate({
|
||||
mutation: loginMutation,
|
||||
mutation: gql`
|
||||
mutation($username: String!, $password: String!, $strategy: String!) {
|
||||
authentication {
|
||||
login(username: $username, password: $password, strategy: $strategy) {
|
||||
responseResult {
|
||||
succeeded
|
||||
errorCode
|
||||
slug
|
||||
message
|
||||
}
|
||||
jwt
|
||||
mustChangePwd
|
||||
mustProvideTFA
|
||||
continuationToken
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
username: this.username,
|
||||
password: this.password,
|
||||
@@ -334,7 +402,11 @@ export default {
|
||||
} else {
|
||||
this.isLoading = true
|
||||
this.$apollo.mutate({
|
||||
mutation: tfaMutation,
|
||||
mutation: gql`
|
||||
{
|
||||
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
continuationToken: this.continuationToken,
|
||||
securityCode: this.securityCode
|
||||
@@ -377,7 +449,11 @@ export default {
|
||||
this.loaderTitle = this.$t('auth:changePwd.loading')
|
||||
this.isLoading = true
|
||||
const resp = await this.$apollo.mutate({
|
||||
mutation: changePasswordMutation,
|
||||
mutation: gql`
|
||||
{
|
||||
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
continuationToken: this.continuationToken,
|
||||
newPassword: this.newPassword
|
||||
@@ -421,8 +497,26 @@ export default {
|
||||
},
|
||||
apollo: {
|
||||
strategies: {
|
||||
query: strategiesQuery,
|
||||
update: (data) => data.authentication.strategies,
|
||||
query: gql`
|
||||
{
|
||||
authentication {
|
||||
activeStrategies {
|
||||
key
|
||||
strategy {
|
||||
key
|
||||
logo
|
||||
color
|
||||
icon
|
||||
useForm
|
||||
}
|
||||
displayName
|
||||
order
|
||||
selfRegistration
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
update: (data) => _.sortBy(data.authentication.activeStrategies, ['order']),
|
||||
watchLoading (isLoading) {
|
||||
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'login-strategies-refresh')
|
||||
}
|
||||
@@ -433,61 +527,73 @@ export default {
|
||||
|
||||
<style lang="scss">
|
||||
.login {
|
||||
background-color: mc('indigo', '900');
|
||||
background-image: url('../static/svg/motif-blocks.svg');
|
||||
background-repeat: repeat;
|
||||
background-size: 200px;
|
||||
background-image: url('/_assets/img/splash/1.jpg');
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
animation: loginBgReveal 20s linear infinite;
|
||||
|
||||
@include keyframes(loginBgReveal) {
|
||||
0% {
|
||||
background-position-y: 0;
|
||||
}
|
||||
100% {
|
||||
background-position-y: 800px;
|
||||
}
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
background-image: url('../static/svg/motif-overlay.svg');
|
||||
background-attachment: fixed;
|
||||
background-size: cover;
|
||||
opacity: .5;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
> .container {
|
||||
&-sd {
|
||||
background-color: rgba(255,255,255,.8);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border-left: 1px solid rgba(255,255,255,.85);
|
||||
border-right: 1px solid rgba(255,255,255,.85);
|
||||
width: 450px;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
margin-left: 5vw;
|
||||
|
||||
@at-root .no-backdropfilter & {
|
||||
background-color: rgba(255,255,255,.95);
|
||||
}
|
||||
|
||||
@include until($tablet) {
|
||||
margin-left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&-logo {
|
||||
padding: 12px 0 0 12px;
|
||||
width: 58px;
|
||||
height: 58px;
|
||||
background-color: #222;
|
||||
margin-left: 12px;
|
||||
border-bottom-left-radius: 7px;
|
||||
border-bottom-right-radius: 7px;
|
||||
}
|
||||
|
||||
&-title {
|
||||
height: 58px;
|
||||
padding-left: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-shadow: .5px .5px #FFF;
|
||||
}
|
||||
|
||||
.social-login-btn {
|
||||
cursor: pointer;
|
||||
transition: opacity .2s ease;
|
||||
&:hover {
|
||||
opacity: .8;
|
||||
}
|
||||
margin: .25rem 0;
|
||||
svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
bottom: 0;
|
||||
path {
|
||||
fill: #FFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.v-text-field.centered input {
|
||||
&-subtitle {
|
||||
padding: 24px 12px 12px 12px;
|
||||
color: #111;
|
||||
font-weight: 500;
|
||||
text-shadow: 1px 1px rgba(255,255,255,.5);
|
||||
background-image: linear-gradient(to bottom, rgba(0,0,0,0), rgba(0,0,0,.15));
|
||||
text-align: center;
|
||||
border-bottom: 1px solid rgba(0,0,0,.3);
|
||||
}
|
||||
|
||||
&-list {
|
||||
border-top: 1px solid rgba(255,255,255,.85);
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
&-form {
|
||||
padding: 12px;
|
||||
border-top: 1px solid rgba(255,255,255,.85);
|
||||
}
|
||||
|
||||
&-main {
|
||||
flex: 1 0 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
Reference in New Issue
Block a user