refactor: deps update + 2FA setup + verify
This commit is contained in:
parent
29afc61abf
commit
f72530bf84
@ -64,7 +64,7 @@ const graphQLLink = ApolloLink.from([
|
|||||||
})
|
})
|
||||||
store.commit('showNotification', {
|
store.commit('showNotification', {
|
||||||
style: 'red',
|
style: 'red',
|
||||||
message: isAuthError ? `You are not authorized to access this resource.` : `An unexpected error occured.`,
|
message: isAuthError ? `You are not authorized to access this resource.` : `An unexpected error occurred.`,
|
||||||
icon: 'alert'
|
icon: 'alert'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -309,6 +309,21 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.admin-providerlogo {
|
||||||
|
width: 250px;
|
||||||
|
height: 50px;
|
||||||
|
float: right;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
margin-left: 16px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.v-application.admin {
|
.v-application.admin {
|
||||||
code {
|
code {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
@ -46,14 +46,16 @@
|
|||||||
hide-details
|
hide-details
|
||||||
inset
|
inset
|
||||||
)
|
)
|
||||||
|
v-card-info(color='blue')
|
||||||
|
div
|
||||||
|
div {{provider.description}}
|
||||||
|
span.caption: a(:href='provider.website') {{provider.website}}
|
||||||
|
v-spacer
|
||||||
|
.admin-providerlogo
|
||||||
|
img(:src='provider.logo', :alt='provider.title')
|
||||||
v-card-text
|
v-card-text
|
||||||
v-form
|
v-form
|
||||||
.analytic-provider-logo
|
.overline.pb-5 {{$t('admin:analytics.providerConfiguration')}}
|
||||||
img(:src='provider.logo', :alt='provider.title')
|
|
||||||
.body-2.pt-3 {{provider.description}}
|
|
||||||
.body-2.pt-3: a(:href='provider.website') {{provider.website}}
|
|
||||||
v-divider.mt-5
|
|
||||||
.overline.py-5 {{$t('admin:analytics.providerConfiguration')}}
|
|
||||||
.body-1.ml-3(v-if='!provider.config || provider.config.length < 1'): em {{$t('admin:analytics.providerNoConfiguration')}}
|
.body-1.ml-3(v-if='!provider.config || provider.config.length < 1'): em {{$t('admin:analytics.providerNoConfiguration')}}
|
||||||
template(v-else, v-for='cfg in provider.config')
|
template(v-else, v-for='cfg in provider.config')
|
||||||
v-select(
|
v-select(
|
||||||
@ -177,21 +179,3 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
|
||||||
|
|
||||||
.analytic-provider-logo {
|
|
||||||
width: 250px;
|
|
||||||
height: 85px;
|
|
||||||
float:right;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
img {
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 50px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
@ -211,7 +211,7 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.$store.commit('showNotification', {
|
this.$store.commit('showNotification', {
|
||||||
style: 'red',
|
style: 'red',
|
||||||
message: _.get(resp, 'data.authentication.createApiKey.responseResult.message', 'An unexpected error occured.'),
|
message: _.get(resp, 'data.authentication.createApiKey.responseResult.message', 'An unexpected error occurred.'),
|
||||||
icon: 'alert'
|
icon: 'alert'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -131,7 +131,7 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.$store.commit('showNotification', {
|
this.$store.commit('showNotification', {
|
||||||
style: 'red',
|
style: 'red',
|
||||||
message: _.get(resp, 'data.authentication.setApiState.responseResult.message', 'An unexpected error occured.'),
|
message: _.get(resp, 'data.authentication.setApiState.responseResult.message', 'An unexpected error occurred.'),
|
||||||
icon: 'alert'
|
icon: 'alert'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -182,7 +182,7 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.$store.commit('showNotification', {
|
this.$store.commit('showNotification', {
|
||||||
style: 'red',
|
style: 'red',
|
||||||
message: _.get(resp, 'data.authentication.revokeApiKey.responseResult.message', 'An unexpected error occured.'),
|
message: _.get(resp, 'data.authentication.revokeApiKey.responseResult.message', 'An unexpected error occurred.'),
|
||||||
icon: 'alert'
|
icon: 'alert'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@
|
|||||||
span {{strategy.strategy.description}}
|
span {{strategy.strategy.description}}
|
||||||
.caption: a(:href='strategy.strategy.website') {{strategy.strategy.website}}
|
.caption: a(:href='strategy.strategy.website') {{strategy.strategy.website}}
|
||||||
v-spacer
|
v-spacer
|
||||||
.authlogo
|
.admin-providerlogo
|
||||||
img(:src='strategy.strategy.logo', :alt='strategy.strategy.title')
|
img(:src='strategy.strategy.logo', :alt='strategy.strategy.title')
|
||||||
v-card-text
|
v-card-text
|
||||||
.overline.mb-5 {{$t('admin:auth.strategyConfiguration')}}
|
.overline.mb-5 {{$t('admin:auth.strategyConfiguration')}}
|
||||||
@ -423,22 +423,3 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
|
||||||
|
|
||||||
.authlogo {
|
|
||||||
width: 250px;
|
|
||||||
height: 60px;
|
|
||||||
float:right;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
align-items: center;
|
|
||||||
margin-left: 16px;
|
|
||||||
|
|
||||||
img {
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 50px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
@ -38,12 +38,14 @@
|
|||||||
v-card.animated.fadeInUp.wait-p2s
|
v-card.animated.fadeInUp.wait-p2s
|
||||||
v-toolbar(color='primary', dense, flat, dark)
|
v-toolbar(color='primary', dense, flat, dark)
|
||||||
.subtitle-1 {{provider.title}}
|
.subtitle-1 {{provider.title}}
|
||||||
v-card-text
|
v-card-info(color='blue')
|
||||||
.providerlogo
|
div
|
||||||
|
div {{provider.description}}
|
||||||
|
span.caption: a(:href='provider.website') {{provider.website}}
|
||||||
|
v-spacer
|
||||||
|
.admin-providerlogo
|
||||||
img(:src='provider.logo', :alt='provider.title')
|
img(:src='provider.logo', :alt='provider.title')
|
||||||
.caption.pt-3 {{provider.description}}
|
v-card-text
|
||||||
.caption.pb-3: a(:href='provider.website') {{provider.website}}
|
|
||||||
v-divider.mt-3
|
|
||||||
.overline.my-5 {{$t('admin:comments.providerConfig')}}
|
.overline.my-5 {{$t('admin:comments.providerConfig')}}
|
||||||
.body-2.ml-3(v-if='!provider.config || provider.config.length < 1'): em {{$t('admin:comments.providerNoConfig')}}
|
.body-2.ml-3(v-if='!provider.config || provider.config.length < 1'): em {{$t('admin:comments.providerNoConfig')}}
|
||||||
template(v-else, v-for='cfg in provider.config')
|
template(v-else, v-for='cfg in provider.config')
|
||||||
@ -202,21 +204,3 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
|
||||||
|
|
||||||
.providerlogo {
|
|
||||||
width: 250px;
|
|
||||||
height: 85px;
|
|
||||||
float:right;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
img {
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 50px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
@ -226,7 +226,7 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (!_.get(resp, 'data.mail.sendTest.responseResult.succeeded', false)) {
|
if (!_.get(resp, 'data.mail.sendTest.responseResult.succeeded', false)) {
|
||||||
throw new Error(_.get(resp, 'data.mail.sendTest.responseResult.message', 'An unexpected error occured.'))
|
throw new Error(_.get(resp, 'data.mail.sendTest.responseResult.message', 'An unexpected error occurred.'))
|
||||||
}
|
}
|
||||||
|
|
||||||
this.testEmail = ''
|
this.testEmail = ''
|
||||||
|
@ -8,7 +8,9 @@
|
|||||||
.headline.primary--text.animated.fadeInLeft {{$t('navigation.title')}}
|
.headline.primary--text.animated.fadeInLeft {{$t('navigation.title')}}
|
||||||
.subtitle-1.grey--text.animated.fadeInLeft.wait-p4s {{$t('navigation.subtitle')}}
|
.subtitle-1.grey--text.animated.fadeInLeft.wait-p4s {{$t('navigation.subtitle')}}
|
||||||
v-spacer
|
v-spacer
|
||||||
v-btn.animated.fadeInDown.wait-p2s.mr-3(icon, outlined, color='grey', @click='refresh')
|
v-btn.animated.fadeInDown.wait-p3s(icon, outlined, color='grey', href='https://docs.requarks.io/navigation', target='_blank')
|
||||||
|
v-icon mdi-help-circle
|
||||||
|
v-btn.mx-3.animated.fadeInDown.wait-p2s.mr-3(icon, outlined, color='grey', @click='refresh')
|
||||||
v-icon mdi-refresh
|
v-icon mdi-refresh
|
||||||
v-btn.animated.fadeInDown(color='success', depressed, @click='save', large)
|
v-btn.animated.fadeInDown(color='success', depressed, @click='save', large)
|
||||||
v-icon(left) mdi-check
|
v-icon(left) mdi-check
|
||||||
@ -30,15 +32,6 @@
|
|||||||
v-list-item-avatar
|
v-list-item-avatar
|
||||||
v-icon(v-if='$vuetify.theme.dark', :color='config.mode === `TREE` ? `teal lighten-3` : `grey darken-2`') mdi-check-circle
|
v-icon(v-if='$vuetify.theme.dark', :color='config.mode === `TREE` ? `teal lighten-3` : `grey darken-2`') mdi-check-circle
|
||||||
v-icon(v-else, :color='config.mode === `TREE` ? `teal` : `grey lighten-3`') mdi-check-circle
|
v-icon(v-else, :color='config.mode === `TREE` ? `teal` : `grey lighten-3`') mdi-check-circle
|
||||||
v-list-item(value='MIXED')
|
|
||||||
v-list-item-avatar
|
|
||||||
img(src='/_assets/svg/icon-user-menu-male-dotted.svg', alt='Custom Navigation')
|
|
||||||
v-list-item-content
|
|
||||||
v-list-item-title {{$t('admin:navigation.modeCustom.title')}}
|
|
||||||
v-list-item-subtitle {{$t('admin:navigation.modeCustom.description')}}
|
|
||||||
v-list-item-avatar
|
|
||||||
v-icon(v-if='$vuetify.theme.dark', :color='config.mode === `MIXED` ? `teal lighten-3` : `grey darken-2`') mdi-check-circle
|
|
||||||
v-icon(v-else, :color='config.mode === `MIXED` ? `teal` : `grey lighten-3`') mdi-check-circle
|
|
||||||
v-list-item(value='STATIC')
|
v-list-item(value='STATIC')
|
||||||
v-list-item-avatar
|
v-list-item-avatar
|
||||||
img(src='/_assets/svg/icon-features-list.svg', alt='Static Navigation')
|
img(src='/_assets/svg/icon-features-list.svg', alt='Static Navigation')
|
||||||
@ -48,6 +41,15 @@
|
|||||||
v-list-item-avatar
|
v-list-item-avatar
|
||||||
v-icon(v-if='$vuetify.theme.dark', :color='config.mode === `STATIC` ? `teal lighten-3` : `grey darken-2`') mdi-check-circle
|
v-icon(v-if='$vuetify.theme.dark', :color='config.mode === `STATIC` ? `teal lighten-3` : `grey darken-2`') mdi-check-circle
|
||||||
v-icon(v-else, :color='config.mode === `STATIC` ? `teal` : `grey lighten-3`') mdi-check-circle
|
v-icon(v-else, :color='config.mode === `STATIC` ? `teal` : `grey lighten-3`') mdi-check-circle
|
||||||
|
v-list-item(value='MIXED')
|
||||||
|
v-list-item-avatar
|
||||||
|
img(src='/_assets/svg/icon-user-menu-male-dotted.svg', alt='Custom Navigation')
|
||||||
|
v-list-item-content
|
||||||
|
v-list-item-title {{$t('admin:navigation.modeCustom.title')}}
|
||||||
|
v-list-item-subtitle {{$t('admin:navigation.modeCustom.description')}}
|
||||||
|
v-list-item-avatar
|
||||||
|
v-icon(v-if='$vuetify.theme.dark', :color='config.mode === `MIXED` ? `teal lighten-3` : `grey darken-2`') mdi-check-circle
|
||||||
|
v-icon(v-else, :color='config.mode === `MIXED` ? `teal` : `grey lighten-3`') mdi-check-circle
|
||||||
v-list-item(value='NONE')
|
v-list-item(value='NONE')
|
||||||
v-list-item-avatar
|
v-list-item-avatar
|
||||||
img(src='/_assets/svg/icon-cancel-dotted.svg', alt='None')
|
img(src='/_assets/svg/icon-cancel-dotted.svg', alt='None')
|
||||||
@ -421,7 +423,7 @@ export default {
|
|||||||
icon: 'check'
|
icon: 'check'
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
throw new Error(_.get(resp, 'data.navigation.updateTree.responseResult.message', 'An unexpected error occured.'))
|
throw new Error(_.get(resp, 'data.navigation.updateTree.responseResult.message', 'An unexpected error occurred.'))
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.$store.commit('pushGraphError', err)
|
this.$store.commit('pushGraphError', err)
|
||||||
|
@ -85,18 +85,12 @@
|
|||||||
hide-details
|
hide-details
|
||||||
inset
|
inset
|
||||||
)
|
)
|
||||||
v-card-text.py-2.pl-4
|
v-card-info(color='blue')
|
||||||
.body-2.pt-3 {{currentRenderer.description}}
|
div
|
||||||
.body-2.pt-1.pb-5: a(href='https://docs.requarks.io/en/rendering', target='_blank') Documentation
|
div {{currentRenderer.description}}
|
||||||
i18next.body-2(path='admin:auth.strategyState', tag='div', v-if='currentRenderer.isEnabled')
|
span.caption: a(href='https://docs.requarks.io/en/rendering', target='_blank') Documentation
|
||||||
v-chip(color='green', small, dark, label, place='state') {{$t('admin:auth.strategyStateActive')}}
|
v-card-text.pb-4.pl-4
|
||||||
span(v-if='selectedCore === `local`', place='locked') {{$t('admin:auth.strategyStateLocked')}}
|
.overline.mb-5 Rendering Module Configuration
|
||||||
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')}}
|
|
||||||
v-divider.mt-3
|
|
||||||
v-card-text.pb-4.pt-2.pl-4
|
|
||||||
.overline.my-5 Rendering Module Configuration
|
|
||||||
.body-2.ml-3(v-if='!currentRenderer.config || currentRenderer.config.length < 1'): em This rendering module has no configuration options you can modify.
|
.body-2.ml-3(v-if='!currentRenderer.config || currentRenderer.config.length < 1'): em This rendering module has no configuration options you can modify.
|
||||||
template(v-else, v-for='(cfg, idx) in currentRenderer.config')
|
template(v-else, v-for='(cfg, idx) in currentRenderer.config')
|
||||||
v-select(
|
v-select(
|
||||||
|
@ -41,13 +41,15 @@
|
|||||||
v-card.animated.fadeInUp.wait-p2s
|
v-card.animated.fadeInUp.wait-p2s
|
||||||
v-toolbar(color='primary', dense, flat, dark)
|
v-toolbar(color='primary', dense, flat, dark)
|
||||||
.subtitle-1 {{engine.title}}
|
.subtitle-1 {{engine.title}}
|
||||||
v-card-text
|
v-card-info(color='blue')
|
||||||
.enginelogo
|
div
|
||||||
|
div {{engine.description}}
|
||||||
|
span.caption: a(:href='engine.website') {{engine.website}}
|
||||||
|
v-spacer
|
||||||
|
.admin-providerlogo
|
||||||
img(:src='engine.logo', :alt='engine.title')
|
img(:src='engine.logo', :alt='engine.title')
|
||||||
.caption.pt-3 {{engine.description}}
|
v-card-text
|
||||||
.caption.pb-3: a(:href='engine.website') {{engine.website}}
|
.overline.mb-5 {{$t('admin:search.engineConfig')}}
|
||||||
v-divider.mt-3
|
|
||||||
.overline.my-5 {{$t('admin:search.engineConfig')}}
|
|
||||||
.body-2.ml-3(v-if='!engine.config || engine.config.length < 1'): em {{$t('admin:search.engineNoConfig')}}
|
.body-2.ml-3(v-if='!engine.config || engine.config.length < 1'): em {{$t('admin:search.engineNoConfig')}}
|
||||||
template(v-else, v-for='cfg in engine.config')
|
template(v-else, v-for='cfg in engine.config')
|
||||||
v-select(
|
v-select(
|
||||||
|
@ -92,12 +92,15 @@
|
|||||||
hide-details
|
hide-details
|
||||||
inset
|
inset
|
||||||
)
|
)
|
||||||
|
v-card-info(color='blue')
|
||||||
|
div
|
||||||
|
div {{target.description}}
|
||||||
|
span.caption: a(:href='target.website') {{target.website}}
|
||||||
|
v-spacer
|
||||||
|
.admin-providerlogo
|
||||||
|
img(:src='target.logo', :alt='target.title')
|
||||||
v-card-text
|
v-card-text
|
||||||
v-form
|
v-form
|
||||||
.targetlogo
|
|
||||||
img(:src='target.logo', :alt='target.title')
|
|
||||||
.body-2.pt-3 {{target.description}}
|
|
||||||
.body-2.pt-3.pb-5: a(:href='target.website') {{target.website}}
|
|
||||||
i18next.body-2(path='admin:storage.targetState', tag='div', v-if='target.isEnabled')
|
i18next.body-2(path='admin:storage.targetState', tag='div', v-if='target.isEnabled')
|
||||||
v-chip(color='green', small, dark, label, place='state') {{$t('admin:storage.targetStateActive')}}
|
v-chip(color='green', small, dark, label, place='state') {{$t('admin:storage.targetStateActive')}}
|
||||||
i18next.body-2(path='admin:storage.targetState', tag='div', v-else)
|
i18next.body-2(path='admin:storage.targetState', tag='div', v-else)
|
||||||
|
@ -153,7 +153,7 @@ export default {
|
|||||||
})
|
})
|
||||||
this.refresh()
|
this.refresh()
|
||||||
} else {
|
} else {
|
||||||
throw new Error(_.get(resp, 'data.pages.deleteTag.responseResult.message', 'An unexpected error occured.'))
|
throw new Error(_.get(resp, 'data.pages.deleteTag.responseResult.message', 'An unexpected error occurred.'))
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.$store.commit('pushGraphError', err)
|
this.$store.commit('pushGraphError', err)
|
||||||
@ -193,7 +193,7 @@ export default {
|
|||||||
})
|
})
|
||||||
this.current.updatedAt = new Date()
|
this.current.updatedAt = new Date()
|
||||||
} else {
|
} else {
|
||||||
throw new Error(_.get(resp, 'data.pages.updateTag.responseResult.message', 'An unexpected error occured.'))
|
throw new Error(_.get(resp, 'data.pages.updateTag.responseResult.message', 'An unexpected error occurred.'))
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.$store.commit('pushGraphError', err)
|
this.$store.commit('pushGraphError', err)
|
||||||
|
@ -212,7 +212,7 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.$store.commit('showNotification', {
|
this.$store.commit('showNotification', {
|
||||||
style: 'red',
|
style: 'red',
|
||||||
message: _.get(resp, 'data.users.create.responseResult.message', 'An unexpected error occured.'),
|
message: _.get(resp, 'data.users.create.responseResult.message', 'An unexpected error occurred.'),
|
||||||
icon: 'alert'
|
icon: 'alert'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -174,7 +174,8 @@
|
|||||||
v-icon mdi-two-factor-authentication
|
v-icon mdi-two-factor-authentication
|
||||||
v-list-item-content
|
v-list-item-content
|
||||||
v-list-item-title {{$t('admin:users.tfa')}}
|
v-list-item-title {{$t('admin:users.tfa')}}
|
||||||
v-list-item-subtitle.red--text Inactive
|
v-list-item-subtitle.green--text(v-if='user.tfaIsActive') Active
|
||||||
|
v-list-item-subtitle.red--text(v-else) Inactive
|
||||||
v-list-item-action
|
v-list-item-action
|
||||||
v-tooltip(top)
|
v-tooltip(top)
|
||||||
template(v-slot:activator='{ on }')
|
template(v-slot:activator='{ on }')
|
||||||
@ -709,7 +710,7 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.$store.commit('showNotification', {
|
this.$store.commit('showNotification', {
|
||||||
style: 'red',
|
style: 'red',
|
||||||
message: _.get(resp, 'data.users.activate.responseResult.message', 'An unexpected error occured.'),
|
message: _.get(resp, 'data.users.activate.responseResult.message', 'An unexpected error occurred.'),
|
||||||
icon: 'warning'
|
icon: 'warning'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -749,7 +750,7 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.$store.commit('showNotification', {
|
this.$store.commit('showNotification', {
|
||||||
style: 'red',
|
style: 'red',
|
||||||
message: _.get(resp, 'data.users.deactivate.responseResult.message', 'An unexpected error occured.'),
|
message: _.get(resp, 'data.users.deactivate.responseResult.message', 'An unexpected error occurred.'),
|
||||||
icon: 'warning'
|
icon: 'warning'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -798,7 +799,7 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.$store.commit('showNotification', {
|
this.$store.commit('showNotification', {
|
||||||
style: 'red',
|
style: 'red',
|
||||||
message: _.get(resp, 'data.users.delete.responseResult.message', 'An unexpected error occured.'),
|
message: _.get(resp, 'data.users.delete.responseResult.message', 'An unexpected error occurred.'),
|
||||||
icon: 'warning'
|
icon: 'warning'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -864,7 +865,7 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.$store.commit('showNotification', {
|
this.$store.commit('showNotification', {
|
||||||
style: 'red',
|
style: 'red',
|
||||||
message: _.get(resp, 'data.users.update.responseResult.message', 'An unexpected error occured.'),
|
message: _.get(resp, 'data.users.update.responseResult.message', 'An unexpected error occurred.'),
|
||||||
icon: 'warning'
|
icon: 'warning'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -935,7 +936,7 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.$store.commit('showNotification', {
|
this.$store.commit('showNotification', {
|
||||||
style: 'red',
|
style: 'red',
|
||||||
message: _.get(resp, 'data.users.verify.responseResult.message', 'An unexpected error occured.'),
|
message: _.get(resp, 'data.users.verify.responseResult.message', 'An unexpected error occurred.'),
|
||||||
icon: 'warning'
|
icon: 'warning'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -962,6 +963,7 @@ export default {
|
|||||||
createdAt
|
createdAt
|
||||||
updatedAt
|
updatedAt
|
||||||
lastLoginAt
|
lastLoginAt
|
||||||
|
tfaIsActive
|
||||||
groups {
|
groups {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
|
@ -345,7 +345,7 @@ export default {
|
|||||||
})
|
})
|
||||||
const respObj = _.get(resp, 'data.system.importUsersFromV1', {})
|
const respObj = _.get(resp, 'data.system.importUsersFromV1', {})
|
||||||
if (!_.get(respObj, 'responseResult.succeeded', false)) {
|
if (!_.get(respObj, 'responseResult.succeeded', false)) {
|
||||||
throw new Error(_.get(respObj, 'responseResult.message', 'An unexpected error occured'))
|
throw new Error(_.get(respObj, 'responseResult.message', 'An unexpected error occurred'))
|
||||||
}
|
}
|
||||||
this.successUsers = _.get(respObj, 'usersCount', 0)
|
this.successUsers = _.get(respObj, 'usersCount', 0)
|
||||||
this.successGroups = _.get(respObj, 'groupsCount', 0)
|
this.successGroups = _.get(respObj, 'groupsCount', 0)
|
||||||
@ -429,7 +429,7 @@ export default {
|
|||||||
})
|
})
|
||||||
const respObj = _.get(respSv, 'data.storage.updateTargets', {})
|
const respObj = _.get(respSv, 'data.storage.updateTargets', {})
|
||||||
if (!_.get(respObj, 'responseResult.succeeded', false)) {
|
if (!_.get(respObj, 'responseResult.succeeded', false)) {
|
||||||
throw new Error(_.get(respObj, 'responseResult.message', 'An unexpected error occured'))
|
throw new Error(_.get(respObj, 'responseResult.message', 'An unexpected error occurred'))
|
||||||
}
|
}
|
||||||
|
|
||||||
this.progress += 10
|
this.progress += 10
|
||||||
@ -480,7 +480,7 @@ export default {
|
|||||||
|
|
||||||
const respImportObj = _.get(respImport, 'data.storage.executeAction', {})
|
const respImportObj = _.get(respImport, 'data.storage.executeAction', {})
|
||||||
if (!_.get(respImportObj, 'responseResult.succeeded', false)) {
|
if (!_.get(respImportObj, 'responseResult.succeeded', false)) {
|
||||||
throw new Error(_.get(respImportObj, 'responseResult.message', 'An unexpected error occured'))
|
throw new Error(_.get(respImportObj, 'responseResult.message', 'An unexpected error occurred'))
|
||||||
}
|
}
|
||||||
|
|
||||||
this.progress += 15
|
this.progress += 15
|
||||||
|
@ -314,7 +314,7 @@ export default {
|
|||||||
this.$vuetify.goTo(`#comment-post-id-${_.get(resp, 'data.comments.create.id', 0)}`, this.scrollOpts)
|
this.$vuetify.goTo(`#comment-post-id-${_.get(resp, 'data.comments.create.id', 0)}`, this.scrollOpts)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
throw new Error(_.get(resp, 'data.comments.create.responseResult.message', 'An unexpected error occured.'))
|
throw new Error(_.get(resp, 'data.comments.create.responseResult.message', 'An unexpected error occurred.'))
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.$store.commit('showNotification', {
|
this.$store.commit('showNotification', {
|
||||||
@ -420,7 +420,7 @@ export default {
|
|||||||
|
|
||||||
this.editCommentCancel()
|
this.editCommentCancel()
|
||||||
} else {
|
} else {
|
||||||
throw new Error(_.get(resp, 'data.comments.delete.responseResult.message', 'An unexpected error occured.'))
|
throw new Error(_.get(resp, 'data.comments.delete.responseResult.message', 'An unexpected error occurred.'))
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn(err)
|
console.warn(err)
|
||||||
@ -482,7 +482,7 @@ export default {
|
|||||||
|
|
||||||
this.comments = _.reject(this.comments, ['id', this.commentToDelete.id])
|
this.comments = _.reject(this.comments, ['id', this.commentToDelete.id])
|
||||||
} else {
|
} else {
|
||||||
throw new Error(_.get(resp, 'data.comments.delete.responseResult.message', 'An unexpected error occured.'))
|
throw new Error(_.get(resp, 'data.comments.delete.responseResult.message', 'An unexpected error occurred.'))
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.$store.commit('showNotification', {
|
this.$store.commit('showNotification', {
|
||||||
|
@ -172,6 +172,19 @@
|
|||||||
span {{$t('common:header.newPage')}}
|
span {{$t('common:header.newPage')}}
|
||||||
v-divider(vertical)
|
v-divider(vertical)
|
||||||
|
|
||||||
|
//- ADMIN
|
||||||
|
|
||||||
|
template(v-if='isAuthenticated && isAdmin')
|
||||||
|
v-tooltip(bottom, v-if='mode !== `admin`')
|
||||||
|
template(v-slot:activator='{ on }')
|
||||||
|
v-btn(icon, tile, height='64', v-on='on', href='/a', :aria-label='$t(`common:header.admin`)')
|
||||||
|
v-icon(color='grey') mdi-cog
|
||||||
|
span {{$t('common:header.admin')}}
|
||||||
|
v-btn(v-else, text, tile, height='64', href='/', :aria-label='$t(`common:actions.exit`)')
|
||||||
|
v-icon(left, color='grey') mdi-exit-to-app
|
||||||
|
span {{$t('common:actions.exit')}}
|
||||||
|
v-divider(vertical)
|
||||||
|
|
||||||
//- ACCOUNT
|
//- ACCOUNT
|
||||||
|
|
||||||
v-menu(v-if='isAuthenticated', offset-y, bottom, min-width='300', transition='slide-y-transition', left)
|
v-menu(v-if='isAuthenticated', offset-y, bottom, min-width='300', transition='slide-y-transition', left)
|
||||||
@ -210,9 +223,6 @@
|
|||||||
v-list-item-action: v-icon(color='blue-grey') mdi-face-profile
|
v-list-item-action: v-icon(color='blue-grey') mdi-face-profile
|
||||||
v-list-item-content
|
v-list-item-content
|
||||||
v-list-item-title(:class='$vuetify.theme.dark ? `blue-grey--text text--lighten-3` : `blue-grey--text`') {{$t('common:header.profile')}}
|
v-list-item-title(:class='$vuetify.theme.dark ? `blue-grey--text text--lighten-3` : `blue-grey--text`') {{$t('common:header.profile')}}
|
||||||
v-list-item(href='/a', v-if='isAuthenticated && isAdmin')
|
|
||||||
v-list-item-action.btn-animate-rotate: v-icon(:color='$vuetify.theme.dark ? `blue-grey lighten-3` : `blue-grey`') mdi-cog
|
|
||||||
v-list-item-title(:class='$vuetify.theme.dark ? `blue-grey--text text--lighten-3` : `blue-grey--text`') {{$t('common:header.admin')}}
|
|
||||||
v-list-item(@click='logout')
|
v-list-item(@click='logout')
|
||||||
v-list-item-action: v-icon(color='red') mdi-logout
|
v-list-item-action: v-icon(color='red') mdi-logout
|
||||||
v-list-item-title.red--text {{$t('common:header.logout')}}
|
v-list-item-title.red--text {{$t('common:header.logout')}}
|
||||||
|
@ -418,7 +418,7 @@ export default {
|
|||||||
window.location.assign(`/${this.locale}/${this.path}`)
|
window.location.assign(`/${this.locale}/${this.path}`)
|
||||||
}, 1000)
|
}, 1000)
|
||||||
} else {
|
} else {
|
||||||
throw new Error(_.get(resp, 'data.pages.restore.responseResult.message', 'An unexpected error occured'))
|
throw new Error(_.get(resp, 'data.pages.restore.responseResult.message', 'An unexpected error occurred'))
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.$store.commit('showNotification', {
|
this.$store.commit('showNotification', {
|
||||||
|
@ -2,17 +2,27 @@
|
|||||||
v-app
|
v-app
|
||||||
.login(:style='`background-image: url(` + bgUrl + `);`')
|
.login(:style='`background-image: url(` + bgUrl + `);`')
|
||||||
.login-sd
|
.login-sd
|
||||||
.d-flex
|
.d-flex.mb-5
|
||||||
.login-logo
|
.login-logo
|
||||||
v-avatar(tile, size='34')
|
v-avatar(tile, size='34')
|
||||||
v-img(:src='logoUrl')
|
v-img(:src='logoUrl')
|
||||||
.login-title
|
.login-title
|
||||||
.text-h6 {{ siteTitle }}
|
.text-h6 {{ siteTitle }}
|
||||||
|
v-alert.mb-0(
|
||||||
|
v-model='errorShown'
|
||||||
|
transition='slide-y-reverse-transition'
|
||||||
|
color='red darken-2'
|
||||||
|
tile
|
||||||
|
dark
|
||||||
|
dense
|
||||||
|
icon='mdi-alert'
|
||||||
|
)
|
||||||
|
.body-2 {{errorMessage}}
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
//- PROVIDERS LIST
|
//- PROVIDERS LIST
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
template(v-if='screen === `login` && strategies.length > 1')
|
template(v-if='screen === `login` && strategies.length > 1')
|
||||||
.login-subtitle.mt-5
|
.login-subtitle
|
||||||
.text-subtitle-1 Select Authentication Provider
|
.text-subtitle-1 Select Authentication Provider
|
||||||
.login-list
|
.login-list
|
||||||
v-list.elevation-1.radius-7(nav)
|
v-list.elevation-1.radius-7(nav)
|
||||||
@ -176,19 +186,51 @@
|
|||||||
v-model='securityCode'
|
v-model='securityCode'
|
||||||
:placeholder='$t("auth:tfa.placeholder")'
|
:placeholder='$t("auth:tfa.placeholder")'
|
||||||
autocomplete='one-time-code'
|
autocomplete='one-time-code'
|
||||||
@keyup.enter='verifySecurityCode'
|
@keyup.enter='verifySecurityCode(false)'
|
||||||
)
|
)
|
||||||
v-btn.mt-2.text-none(
|
v-btn.mt-2.text-none(
|
||||||
width='100%'
|
width='100%'
|
||||||
large
|
large
|
||||||
color='primary'
|
color='primary'
|
||||||
dark
|
dark
|
||||||
@click='verifySecurityCode'
|
@click='verifySecurityCode(false)'
|
||||||
|
:loading='isLoading'
|
||||||
|
) {{ $t('auth:tfa.verifyToken') }}
|
||||||
|
|
||||||
|
//-------------------------------------------------
|
||||||
|
//- SETUP TFA FORM
|
||||||
|
//-------------------------------------------------
|
||||||
|
v-dialog(v-model='isTFASetupShown', max-width='600', persistent)
|
||||||
|
v-card
|
||||||
|
.login-tfa.text-center.pa-5
|
||||||
|
.subtitle-1.primary--text Your administrator has required Two-Factor Authentication (2FA) to be enabled on your account.
|
||||||
|
v-divider.my-5
|
||||||
|
.subtitle-2 1) Scan the QR code below from your mobile 2FA application:
|
||||||
|
.caption (e.g. #[a(href='https://authy.com/', target='_blank', noopener) Authy], #[a(href='https://support.google.com/accounts/answer/1066447', target='_blank', noopener) Google Authenticator], #[a(href='https://www.microsoft.com/en-us/account/authenticator', target='_blank', noopener) Microsoft Authenticator], etc.)
|
||||||
|
.login-tfa-qr.mt-5(v-if='isTFASetupShown', v-html='tfaQRImage')
|
||||||
|
.subtitle-2.mt-5 2) Enter the security code generated from your trusted device:
|
||||||
|
v-text-field.login-tfa-field.mt-2(
|
||||||
|
solo
|
||||||
|
flat
|
||||||
|
background-color='white'
|
||||||
|
hide-details
|
||||||
|
ref='iptTFASetup'
|
||||||
|
v-model='securityCode'
|
||||||
|
:placeholder='$t("auth:tfa.placeholder")'
|
||||||
|
autocomplete='one-time-code'
|
||||||
|
@keyup.enter='verifySecurityCode(true)'
|
||||||
|
)
|
||||||
|
v-btn.mt-2.text-none(
|
||||||
|
width='100%'
|
||||||
|
large
|
||||||
|
color='primary'
|
||||||
|
dark
|
||||||
|
@click='verifySecurityCode(true)'
|
||||||
:loading='isLoading'
|
:loading='isLoading'
|
||||||
) {{ $t('auth:tfa.verifyToken') }}
|
) {{ $t('auth:tfa.verifyToken') }}
|
||||||
|
|
||||||
loader(v-model='isLoading', :color='loaderColor', :title='loaderTitle', :subtitle='$t(`auth:pleaseWait`)')
|
loader(v-model='isLoading', :color='loaderColor', :title='loaderTitle', :subtitle='$t(`auth:pleaseWait`)')
|
||||||
notify
|
notify(style='padding-top: 64px;')
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -231,7 +273,11 @@ export default {
|
|||||||
isShown: false,
|
isShown: false,
|
||||||
newPassword: '',
|
newPassword: '',
|
||||||
newPasswordVerify: '',
|
newPasswordVerify: '',
|
||||||
isTFAShown: false
|
isTFAShown: false,
|
||||||
|
isTFASetupShown: false,
|
||||||
|
tfaQRImage: '',
|
||||||
|
errorShown: false,
|
||||||
|
errorMessage: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -282,26 +328,21 @@ export default {
|
|||||||
* LOGIN
|
* LOGIN
|
||||||
*/
|
*/
|
||||||
async login () {
|
async login () {
|
||||||
|
this.errorShown = false
|
||||||
if (this.username.length < 2) {
|
if (this.username.length < 2) {
|
||||||
this.$store.commit('showNotification', {
|
this.errorMessage = this.$t('auth:invalidEmailUsername')
|
||||||
style: 'red',
|
this.errorShown = true
|
||||||
message: this.$t('auth:invalidEmailUsername'),
|
|
||||||
icon: 'alert'
|
|
||||||
})
|
|
||||||
this.$refs.iptEmail.focus()
|
this.$refs.iptEmail.focus()
|
||||||
} else if (this.password.length < 2) {
|
} else if (this.password.length < 2) {
|
||||||
this.$store.commit('showNotification', {
|
this.errorMessage = this.$t('auth:invalidPassword')
|
||||||
style: 'red',
|
this.errorShown = true
|
||||||
message: this.$t('auth:invalidPassword'),
|
|
||||||
icon: 'alert'
|
|
||||||
})
|
|
||||||
this.$refs.iptPassword.focus()
|
this.$refs.iptPassword.focus()
|
||||||
} else {
|
} else {
|
||||||
this.loaderColor = 'grey darken-4'
|
this.loaderColor = 'grey darken-4'
|
||||||
this.loaderTitle = this.$t('auth:signingIn')
|
this.loaderTitle = this.$t('auth:signingIn')
|
||||||
this.isLoading = true
|
this.isLoading = true
|
||||||
try {
|
try {
|
||||||
let resp = await this.$apollo.mutate({
|
const resp = await this.$apollo.mutate({
|
||||||
mutation: gql`
|
mutation: gql`
|
||||||
mutation($username: String!, $password: String!, $strategy: String!) {
|
mutation($username: String!, $password: String!, $strategy: String!) {
|
||||||
authentication {
|
authentication {
|
||||||
@ -315,8 +356,10 @@ export default {
|
|||||||
jwt
|
jwt
|
||||||
mustChangePwd
|
mustChangePwd
|
||||||
mustProvideTFA
|
mustProvideTFA
|
||||||
|
mustSetupTFA
|
||||||
continuationToken
|
continuationToken
|
||||||
redirect
|
redirect
|
||||||
|
tfaQRImage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -328,38 +371,9 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (_.has(resp, 'data.authentication.login')) {
|
if (_.has(resp, 'data.authentication.login')) {
|
||||||
let respObj = _.get(resp, 'data.authentication.login', {})
|
const respObj = _.get(resp, 'data.authentication.login', {})
|
||||||
if (respObj.responseResult.succeeded === true) {
|
if (respObj.responseResult.succeeded === true) {
|
||||||
this.continuationToken = respObj.continuationToken
|
this.handleLoginResponse(respObj)
|
||||||
if (respObj.mustChangePwd === true) {
|
|
||||||
this.screen = 'changePwd'
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.$refs.iptNewPassword.focus()
|
|
||||||
})
|
|
||||||
this.isLoading = false
|
|
||||||
} else if (respObj.mustProvideTFA === true) {
|
|
||||||
this.screen = 'tfa'
|
|
||||||
this.securityCode = ''
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.$refs.iptTFA.focus()
|
|
||||||
})
|
|
||||||
this.isLoading = false
|
|
||||||
} else {
|
|
||||||
this.loaderColor = 'green darken-1'
|
|
||||||
this.loaderTitle = this.$t('auth:loginSuccess')
|
|
||||||
Cookies.set('jwt', respObj.jwt, { expires: 365 })
|
|
||||||
_.delay(() => {
|
|
||||||
const loginRedirect = Cookies.get('loginRedirect')
|
|
||||||
if (loginRedirect) {
|
|
||||||
Cookies.remove('loginRedirect')
|
|
||||||
window.location.replace(loginRedirect)
|
|
||||||
} else if (respObj.redirect) {
|
|
||||||
window.location.replace(respObj.redirect)
|
|
||||||
} else {
|
|
||||||
window.location.replace('/')
|
|
||||||
}
|
|
||||||
}, 1000)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error(respObj.responseResult.message)
|
throw new Error(respObj.responseResult.message)
|
||||||
}
|
}
|
||||||
@ -380,58 +394,70 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* VERIFY TFA CODE
|
* VERIFY TFA CODE
|
||||||
*/
|
*/
|
||||||
verifySecurityCode () {
|
async verifySecurityCode (setup = false) {
|
||||||
if (this.securityCode.length !== 6) {
|
if (this.securityCode.length !== 6) {
|
||||||
this.$store.commit('showNotification', {
|
this.$store.commit('showNotification', {
|
||||||
style: 'red',
|
style: 'red',
|
||||||
message: 'Enter a valid security code.',
|
message: 'Enter a valid security code.',
|
||||||
icon: 'warning'
|
icon: 'alert'
|
||||||
})
|
})
|
||||||
this.$refs.iptTFA.focus()
|
if (setup) {
|
||||||
|
this.$refs.iptTFASetup.focus()
|
||||||
|
} else {
|
||||||
|
this.$refs.iptTFA.focus()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
this.loaderColor = 'grey darken-4'
|
||||||
|
this.loaderTitle = this.$t('auth:signingIn')
|
||||||
this.isLoading = true
|
this.isLoading = true
|
||||||
this.$apollo.mutate({
|
try {
|
||||||
mutation: gql`
|
const resp = await this.$apollo.mutate({
|
||||||
{
|
mutation: gql`
|
||||||
authentication {
|
mutation(
|
||||||
activeStrategies {
|
$continuationToken: String!
|
||||||
key
|
$securityCode: String!
|
||||||
|
$setup: Boolean
|
||||||
|
) {
|
||||||
|
authentication {
|
||||||
|
loginTFA(
|
||||||
|
continuationToken: $continuationToken
|
||||||
|
securityCode: $securityCode
|
||||||
|
setup: $setup
|
||||||
|
) {
|
||||||
|
responseResult {
|
||||||
|
succeeded
|
||||||
|
errorCode
|
||||||
|
slug
|
||||||
|
message
|
||||||
|
}
|
||||||
|
jwt
|
||||||
|
mustChangePwd
|
||||||
|
continuationToken
|
||||||
|
redirect
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
`,
|
||||||
|
variables: {
|
||||||
|
continuationToken: this.continuationToken,
|
||||||
|
securityCode: this.securityCode,
|
||||||
|
setup
|
||||||
}
|
}
|
||||||
`,
|
})
|
||||||
variables: {
|
|
||||||
continuationToken: this.continuationToken,
|
|
||||||
securityCode: this.securityCode
|
|
||||||
}
|
|
||||||
}).then(resp => {
|
|
||||||
if (_.has(resp, 'data.authentication.loginTFA')) {
|
if (_.has(resp, 'data.authentication.loginTFA')) {
|
||||||
let respObj = _.get(resp, 'data.authentication.loginTFA', {})
|
let respObj = _.get(resp, 'data.authentication.loginTFA', {})
|
||||||
if (respObj.responseResult.succeeded === true) {
|
if (respObj.responseResult.succeeded === true) {
|
||||||
this.$store.commit('showNotification', {
|
this.handleLoginResponse(respObj)
|
||||||
message: 'Login successful!',
|
|
||||||
style: 'success',
|
|
||||||
icon: 'check'
|
|
||||||
})
|
|
||||||
_.delay(() => {
|
|
||||||
const loginRedirect = Cookies.get('loginRedirect')
|
|
||||||
if (loginRedirect) {
|
|
||||||
Cookies.remove('loginRedirect')
|
|
||||||
window.location.replace(loginRedirect)
|
|
||||||
} else if (respObj.redirect) {
|
|
||||||
window.location.replace(respObj.redirect)
|
|
||||||
} else {
|
|
||||||
window.location.replace('/')
|
|
||||||
}
|
|
||||||
}, 1000)
|
|
||||||
this.isLoading = false
|
|
||||||
} else {
|
} else {
|
||||||
|
if (!setup) {
|
||||||
|
this.isTFAShown = false
|
||||||
|
}
|
||||||
throw new Error(respObj.responseResult.message)
|
throw new Error(respObj.responseResult.message)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error(this.$t('auth:genericError'))
|
throw new Error(this.$t('auth:genericError'))
|
||||||
}
|
}
|
||||||
}).catch(err => {
|
} catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
this.$store.commit('showNotification', {
|
this.$store.commit('showNotification', {
|
||||||
style: 'red',
|
style: 'red',
|
||||||
@ -439,7 +465,7 @@ export default {
|
|||||||
icon: 'alert'
|
icon: 'alert'
|
||||||
})
|
})
|
||||||
this.isLoading = false
|
this.isLoading = false
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@ -498,6 +524,46 @@ export default {
|
|||||||
message: 'Coming soon!',
|
message: 'Coming soon!',
|
||||||
icon: 'ferry'
|
icon: 'ferry'
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
handleLoginResponse (respObj) {
|
||||||
|
this.continuationToken = respObj.continuationToken
|
||||||
|
if (respObj.mustChangePwd === true) {
|
||||||
|
this.screen = 'changePwd'
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.iptNewPassword.focus()
|
||||||
|
})
|
||||||
|
this.isLoading = false
|
||||||
|
} else if (respObj.mustProvideTFA === true) {
|
||||||
|
this.securityCode = ''
|
||||||
|
this.isTFAShown = true
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.iptTFA.focus()
|
||||||
|
}, 500)
|
||||||
|
this.isLoading = false
|
||||||
|
} else if (respObj.mustSetupTFA === true) {
|
||||||
|
this.securityCode = ''
|
||||||
|
this.isTFASetupShown = true
|
||||||
|
this.tfaQRImage = respObj.tfaQRImage
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.iptTFASetup.focus()
|
||||||
|
}, 500)
|
||||||
|
this.isLoading = false
|
||||||
|
} else {
|
||||||
|
this.loaderColor = 'green darken-1'
|
||||||
|
this.loaderTitle = this.$t('auth:loginSuccess')
|
||||||
|
Cookies.set('jwt', respObj.jwt, { expires: 365 })
|
||||||
|
_.delay(() => {
|
||||||
|
const loginRedirect = Cookies.get('loginRedirect')
|
||||||
|
if (loginRedirect) {
|
||||||
|
Cookies.remove('loginRedirect')
|
||||||
|
window.location.replace(loginRedirect)
|
||||||
|
} else if (respObj.redirect) {
|
||||||
|
window.location.replace(respObj.redirect)
|
||||||
|
} else {
|
||||||
|
window.location.replace('/')
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
apollo: {
|
apollo: {
|
||||||
@ -619,6 +685,15 @@ export default {
|
|||||||
&-field input {
|
&-field input {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-qr {
|
||||||
|
background-color: #FFF;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
v-text-field.md2.mt-2(
|
v-text-field.md2.mt-2(
|
||||||
solo
|
solo
|
||||||
flat
|
flat
|
||||||
prepend-icon='mdi-textbox-password'
|
prepend-icon='mdi-form-textbox-password'
|
||||||
:background-color='$vuetify.theme.dark ? `grey darken-3` : `grey lighten-4`'
|
:background-color='$vuetify.theme.dark ? `grey darken-3` : `grey lighten-4`'
|
||||||
ref='iptPassword'
|
ref='iptPassword'
|
||||||
v-model='password'
|
v-model='password'
|
||||||
@ -49,7 +49,7 @@
|
|||||||
v-text-field.md2.mt-2(
|
v-text-field.md2.mt-2(
|
||||||
solo
|
solo
|
||||||
flat
|
flat
|
||||||
prepend-icon='mdi-textbox-password'
|
prepend-icon='mdi-form-textbox-password'
|
||||||
:background-color='$vuetify.theme.dark ? `grey darken-3` : `grey lighten-4`'
|
:background-color='$vuetify.theme.dark ? `grey darken-3` : `grey lighten-4`'
|
||||||
hide-details
|
hide-details
|
||||||
ref='iptVerifyPassword'
|
ref='iptVerifyPassword'
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
v-card.elevation-20.radius-7.animated.fadeInUp
|
v-card.elevation-20.radius-7.animated.fadeInUp
|
||||||
v-alert(v-if='isDevMode', tile, dark, color='red darken-3', icon='mdi-alert', prominent)
|
v-alert(v-if='isDevMode', tile, dark, color='red darken-3', icon='mdi-alert', prominent)
|
||||||
.body-2 You are running an unstable, unreleased development version. This code base is #[strong NOT] for production use!
|
.body-2 You are running an unstable, unreleased development version. This code base is #[strong NOT] for production use!
|
||||||
.body-2.mt-3 Cloning the master branch directly from GitHub is #[strong NOT] the proper way to install Wiki.js!
|
.body-2.mt-3 Cloning the dev branch directly from GitHub is #[strong NOT] the proper way to install Wiki.js!
|
||||||
.body-2 Read the #[a(href='https://docs.requarks.io/install', style='color: #FFF;') documentation] on correctly installing the latest stable version.
|
.body-2 Read the #[a(href='https://docs.requarks.io/install', style='color: #FFF;') documentation] on correctly installing the latest stable version.
|
||||||
.text-center
|
.text-center
|
||||||
img.setup-logo.animated.fadeInUp.wait-p2s(src='/_assets/svg/logo-wikijs-full.svg', alt='Wiki.js Logo')
|
img.setup-logo.animated.fadeInUp.wait-p2s(src='/_assets/svg/logo-wikijs-full.svg', alt='Wiki.js Logo')
|
||||||
@ -249,7 +249,7 @@ export default {
|
|||||||
height: 100vh;
|
height: 100vh;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
background-image: url(/_assets/svg/motif-grid.svg) !important;
|
background-image: url(../static/svg/motif-grid.svg) !important;
|
||||||
background-size: 100px;
|
background-size: 100px;
|
||||||
background-repeat: repeat;
|
background-repeat: repeat;
|
||||||
animation: bg-anim 100s linear infinite;
|
animation: bg-anim 100s linear infinite;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
require('./scss/legacy.scss')
|
require('./scss/legacy.scss')
|
||||||
|
require('./scss/fonts/default.scss')
|
||||||
|
|
||||||
window.WIKI = null
|
window.WIKI = null
|
||||||
|
@ -1,39 +1,39 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Tajawal';
|
font-family: 'Tajawal';
|
||||||
src: url('/_assets/fonts/arabic/Tajawal-Bold.woff2') format('woff2'),
|
src: url('../../fonts/arabic/Tajawal-Bold.woff2') format('woff2'),
|
||||||
url('/_assets/fonts/arabic/Tajawal-Bold.woff') format('woff');
|
url('../../fonts/arabic/Tajawal-Bold.woff') format('woff');
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Tajawal';
|
font-family: 'Tajawal';
|
||||||
src: url('/_assets/fonts/arabic/Tajawal-Regular.woff2') format('woff2'),
|
src: url('../../fonts/arabic/Tajawal-Regular.woff2') format('woff2'),
|
||||||
url('/_assets/fonts/arabic/Tajawal-Regular.woff') format('woff');
|
url('../../fonts/arabic/Tajawal-Regular.woff') format('woff');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Tajawal';
|
font-family: 'Tajawal';
|
||||||
src: url('/_assets/fonts/arabic/Tajawal-Medium.woff2') format('woff2'),
|
src: url('../../fonts/arabic/Tajawal-Medium.woff2') format('woff2'),
|
||||||
url('/_assets/fonts/arabic/Tajawal-Medium.woff') format('woff');
|
url('../../fonts/arabic/Tajawal-Medium.woff') format('woff');
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'BalooBhaijaan';
|
font-family: 'BalooBhaijaan';
|
||||||
src: url('/_assets/fonts/arabic/BalooBhaijaan-Regular.woff2') format('woff2'),
|
src: url('../../fonts/arabic/BalooBhaijaan-Regular.woff2') format('woff2'),
|
||||||
url('/_assets/fonts/arabic/BalooBhaijaan-Regular.woff') format('woff');
|
url('../../fonts/arabic/BalooBhaijaan-Regular.woff') format('woff');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto Mono';
|
font-family: 'Roboto Mono';
|
||||||
src: url('/_assets/fonts/default/RobotoMono-Regular.woff2') format('woff2'),
|
src: url('../../fonts/default/RobotoMono-Regular.woff2') format('woff2'),
|
||||||
url('/_assets/fonts/default/RobotoMono-Regular.woff') format('woff');
|
url('../../fonts/default/RobotoMono-Regular.woff') format('woff');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
@ -1,55 +1,55 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: 'Roboto';
|
||||||
src: url('/_assets/fonts/default/Roboto-MediumItalic.woff2') format('woff2'),
|
src: url('../../fonts/default/Roboto-MediumItalic.woff2') format('woff2'),
|
||||||
url('/_assets/fonts/default/Roboto-MediumItalic.woff') format('woff');
|
url('../../fonts/default/Roboto-MediumItalic.woff') format('woff');
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: 'Roboto';
|
||||||
src: url('/_assets/fonts/default/Roboto-Italic.woff2') format('woff2'),
|
src: url('../../fonts/default/Roboto-Italic.woff2') format('woff2'),
|
||||||
url('/_assets/fonts/default/Roboto-Italic.woff') format('woff');
|
url('../../fonts/default/Roboto-Italic.woff') format('woff');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: 'Roboto';
|
||||||
src: url('/_assets/fonts/default/Roboto-Bold.woff2') format('woff2'),
|
src: url('../../fonts/default/Roboto-Bold.woff2') format('woff2'),
|
||||||
url('/_assets/fonts/default/Roboto-Bold.woff') format('woff');
|
url('../../fonts/default/Roboto-Bold.woff') format('woff');
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: 'Roboto';
|
||||||
src: url('/_assets/fonts/default/Roboto-Regular.woff2') format('woff2'),
|
src: url('../../fonts/default/Roboto-Regular.woff2') format('woff2'),
|
||||||
url('/_assets/fonts/default/Roboto-Regular.woff') format('woff');
|
url('../../fonts/default/Roboto-Regular.woff') format('woff');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: 'Roboto';
|
||||||
src: url('/_assets/fonts/default/Roboto-BoldItalic.woff2') format('woff2'),
|
src: url('../../fonts/default/Roboto-BoldItalic.woff2') format('woff2'),
|
||||||
url('/_assets/fonts/default/Roboto-BoldItalic.woff') format('woff');
|
url('../../fonts/default/Roboto-BoldItalic.woff') format('woff');
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: 'Roboto';
|
||||||
src: url('/_assets/fonts/default/Roboto-Medium.woff2') format('woff2'),
|
src: url('../../fonts/default/Roboto-Medium.woff2') format('woff2'),
|
||||||
url('/_assets/fonts/default/Roboto-Medium.woff') format('woff');
|
url('../../fonts/default/Roboto-Medium.woff') format('woff');
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto Mono';
|
font-family: 'Roboto Mono';
|
||||||
src: url('/_assets/fonts/default/RobotoMono-Regular.woff2') format('woff2'),
|
src: url('../../fonts/default/RobotoMono-Regular.woff2') format('woff2'),
|
||||||
url('/_assets/fonts/default/RobotoMono-Regular.woff') format('woff');
|
url('../../fonts/default/RobotoMono-Regular.woff') format('woff');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
@import "global";
|
@import "global";
|
||||||
|
|
||||||
@import "./base/icons.scss";
|
@import "./base/icons.scss";
|
||||||
@import "./fonts/default.scss";
|
|
||||||
@import '~katex/dist/katex.min.css';
|
@import '~katex/dist/katex.min.css';
|
||||||
@import '~@mdi/font/css/materialdesignicons.css';
|
@import '~@mdi/font/css/materialdesignicons.css';
|
||||||
|
|
||||||
|
@ -162,10 +162,7 @@ module.exports = {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
|
test: /\.(woff2|woff|ttf|eot)(\?v=\d+\.\d+\.\d+)?$/,
|
||||||
exclude: [
|
|
||||||
path.join(process.cwd(), 'client')
|
|
||||||
],
|
|
||||||
use: [{
|
use: [{
|
||||||
loader: 'file-loader',
|
loader: 'file-loader',
|
||||||
options: {
|
options: {
|
||||||
|
@ -168,10 +168,7 @@ module.exports = {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
|
test: /\.(woff2|woff|ttf|eot)(\?v=\d+\.\d+\.\d+)?$/,
|
||||||
exclude: [
|
|
||||||
path.join(process.cwd(), 'client')
|
|
||||||
],
|
|
||||||
use: [{
|
use: [{
|
||||||
loader: 'file-loader',
|
loader: 'file-loader',
|
||||||
options: {
|
options: {
|
||||||
|
132
package.json
132
package.json
@ -38,26 +38,26 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aoberoi/passport-slack": "1.0.5",
|
"@aoberoi/passport-slack": "1.0.5",
|
||||||
"@azure/storage-blob": "12.1.2",
|
"@azure/storage-blob": "12.1.2",
|
||||||
"@bugsnag/js": "7.2.0",
|
"@bugsnag/js": "7.3.0",
|
||||||
"@exlinc/keycloak-passport": "1.0.2",
|
"@exlinc/keycloak-passport": "1.0.2",
|
||||||
"@root/csr": "0.8.1",
|
"@root/csr": "0.8.1",
|
||||||
"@root/keypairs": "0.9.0",
|
"@root/keypairs": "0.10.1",
|
||||||
"@root/pem": "1.0.4",
|
"@root/pem": "1.0.4",
|
||||||
"acme": "3.0.3",
|
"acme": "3.0.3",
|
||||||
"akismet-api": "5.0.0",
|
"akismet-api": "5.0.0",
|
||||||
"algoliasearch": "4.3.0",
|
"algoliasearch": "4.4.0",
|
||||||
"apollo-fetch": "0.7.0",
|
"apollo-fetch": "0.7.0",
|
||||||
"apollo-server": "2.15.1",
|
"apollo-server": "2.16.1",
|
||||||
"apollo-server-express": "2.15.1",
|
"apollo-server-express": "2.16.1",
|
||||||
"auto-load": "3.0.4",
|
"auto-load": "3.0.4",
|
||||||
"aws-sdk": "2.713.0",
|
"aws-sdk": "2.738.0",
|
||||||
"azure-search-client": "3.1.5",
|
"azure-search-client": "3.1.5",
|
||||||
"bcryptjs-then": "1.0.1",
|
"bcryptjs-then": "1.0.1",
|
||||||
"bluebird": "3.7.2",
|
"bluebird": "3.7.2",
|
||||||
"body-parser": "1.19.0",
|
"body-parser": "1.19.0",
|
||||||
"chalk": "4.1.0",
|
"chalk": "4.1.0",
|
||||||
"cheerio": "1.0.0-rc.3",
|
"cheerio": "1.0.0-rc.3",
|
||||||
"chokidar": "3.4.0",
|
"chokidar": "3.4.2",
|
||||||
"chromium-pickle-js": "0.2.0",
|
"chromium-pickle-js": "0.2.0",
|
||||||
"clean-css": "4.2.3",
|
"clean-css": "4.2.3",
|
||||||
"command-exists": "1.2.9",
|
"command-exists": "1.2.9",
|
||||||
@ -69,7 +69,7 @@
|
|||||||
"custom-error-instance": "2.1.2",
|
"custom-error-instance": "2.1.2",
|
||||||
"dependency-graph": "0.9.0",
|
"dependency-graph": "0.9.0",
|
||||||
"diff": "4.0.2",
|
"diff": "4.0.2",
|
||||||
"diff2html": "3.1.9",
|
"diff2html": "3.1.12",
|
||||||
"dompurify": "2.0.12",
|
"dompurify": "2.0.12",
|
||||||
"dotize": "0.3.0",
|
"dotize": "0.3.0",
|
||||||
"elasticsearch6": "npm:@elastic/elasticsearch@6",
|
"elasticsearch6": "npm:@elastic/elasticsearch@6",
|
||||||
@ -79,7 +79,7 @@
|
|||||||
"express": "4.17.1",
|
"express": "4.17.1",
|
||||||
"express-brute": "1.0.1",
|
"express-brute": "1.0.1",
|
||||||
"express-session": "1.17.1",
|
"express-session": "1.17.1",
|
||||||
"file-type": "14.6.2",
|
"file-type": "14.7.1",
|
||||||
"filesize": "6.1.0",
|
"filesize": "6.1.0",
|
||||||
"fs-extra": "9.0.1",
|
"fs-extra": "9.0.1",
|
||||||
"getos": "3.2.1",
|
"getos": "3.2.1",
|
||||||
@ -87,22 +87,22 @@
|
|||||||
"graphql-list-fields": "2.0.2",
|
"graphql-list-fields": "2.0.2",
|
||||||
"graphql-rate-limit-directive": "1.2.1",
|
"graphql-rate-limit-directive": "1.2.1",
|
||||||
"graphql-subscriptions": "1.1.0",
|
"graphql-subscriptions": "1.1.0",
|
||||||
"graphql-tools": "6.0.12",
|
"graphql-tools": "6.0.18",
|
||||||
"he": "1.2.0",
|
"he": "1.2.0",
|
||||||
"highlight.js": "10.1.1",
|
"highlight.js": "10.1.2",
|
||||||
"i18next": "19.6.0",
|
"i18next": "19.7.0",
|
||||||
"i18next-express-middleware": "2.0.0",
|
"i18next-express-middleware": "2.0.0",
|
||||||
"i18next-node-fs-backend": "2.1.3",
|
"i18next-node-fs-backend": "2.1.3",
|
||||||
"image-size": "0.8.3",
|
"image-size": "0.8.3",
|
||||||
"js-base64": "2.6.3",
|
"js-base64": "3.4.5",
|
||||||
"js-binary": "1.2.0",
|
"js-binary": "1.2.0",
|
||||||
"js-yaml": "3.14.0",
|
"js-yaml": "3.14.0",
|
||||||
"jsdom": "16.3.0",
|
"jsdom": "16.4.0",
|
||||||
"jsonwebtoken": "8.5.1",
|
"jsonwebtoken": "8.5.1",
|
||||||
"katex": "0.11.1",
|
"katex": "0.12.0",
|
||||||
"klaw": "3.0.0",
|
"klaw": "3.0.0",
|
||||||
"knex": "0.21.2",
|
"knex": "0.21.5",
|
||||||
"lodash": "4.17.19",
|
"lodash": "4.17.20",
|
||||||
"luxon": "1.24.1",
|
"luxon": "1.24.1",
|
||||||
"markdown-it": "11.0.0",
|
"markdown-it": "11.0.0",
|
||||||
"markdown-it-abbr": "1.0.4",
|
"markdown-it-abbr": "1.0.4",
|
||||||
@ -122,16 +122,16 @@
|
|||||||
"mime-types": "2.1.27",
|
"mime-types": "2.1.27",
|
||||||
"moment": "2.27.0",
|
"moment": "2.27.0",
|
||||||
"moment-timezone": "0.5.31",
|
"moment-timezone": "0.5.31",
|
||||||
"mongodb": "3.5.9",
|
"mongodb": "3.6.0",
|
||||||
"ms": "2.1.2",
|
"ms": "2.1.2",
|
||||||
"mssql": "6.2.0",
|
"mssql": "6.2.1",
|
||||||
"multer": "1.4.2",
|
"multer": "1.4.2",
|
||||||
"mysql2": "2.1.0",
|
"mysql2": "2.1.0",
|
||||||
"nanoid": "3.1.10",
|
"nanoid": "3.1.12",
|
||||||
"node-2fa": "1.1.2",
|
"node-2fa": "1.1.2",
|
||||||
"node-cache": "5.1.2",
|
"node-cache": "5.1.2",
|
||||||
"nodemailer": "6.4.10",
|
"nodemailer": "6.4.11",
|
||||||
"objection": "2.2.1",
|
"objection": "2.2.3",
|
||||||
"passport": "0.4.1",
|
"passport": "0.4.1",
|
||||||
"passport-auth0": "1.3.3",
|
"passport-auth0": "1.3.3",
|
||||||
"passport-azure-ad": "4.2.1",
|
"passport-azure-ad": "4.2.1",
|
||||||
@ -149,13 +149,13 @@
|
|||||||
"passport-oauth2": "1.5.0",
|
"passport-oauth2": "1.5.0",
|
||||||
"passport-okta-oauth": "0.0.1",
|
"passport-okta-oauth": "0.0.1",
|
||||||
"passport-openidconnect": "0.0.2",
|
"passport-openidconnect": "0.0.2",
|
||||||
"passport-saml": "1.3.3",
|
"passport-saml": "1.3.4",
|
||||||
"passport-twitch-oauth": "1.0.0",
|
"passport-twitch-oauth": "1.0.0",
|
||||||
"pem-jwk": "2.0.0",
|
"pem-jwk": "2.0.0",
|
||||||
"pg": "8.3.0",
|
"pg": "8.3.2",
|
||||||
"pg-hstore": "2.3.3",
|
"pg-hstore": "2.3.3",
|
||||||
"pg-pubsub": "0.5.0",
|
"pg-pubsub": "0.5.0",
|
||||||
"pg-query-stream": "3.2.0",
|
"pg-query-stream": "3.2.2",
|
||||||
"pg-tsquery": "8.1.0",
|
"pg-tsquery": "8.1.0",
|
||||||
"pug": "3.0.0",
|
"pug": "3.0.0",
|
||||||
"punycode": "2.1.1",
|
"punycode": "2.1.1",
|
||||||
@ -163,33 +163,33 @@
|
|||||||
"raven": "2.6.4",
|
"raven": "2.6.4",
|
||||||
"remove-markdown": "0.3.0",
|
"remove-markdown": "0.3.0",
|
||||||
"request": "2.88.2",
|
"request": "2.88.2",
|
||||||
"request-promise": "4.2.5",
|
"request-promise": "4.2.6",
|
||||||
"safe-regex": "2.1.1",
|
"safe-regex": "2.1.1",
|
||||||
"sanitize-filename": "1.6.3",
|
"sanitize-filename": "1.6.3",
|
||||||
"scim-query-filter-parser": "2.0.4",
|
"scim-query-filter-parser": "2.0.4",
|
||||||
"semver": "7.3.2",
|
"semver": "7.3.2",
|
||||||
"serve-favicon": "2.5.0",
|
"serve-favicon": "2.5.0",
|
||||||
"simple-git": "2.12.0",
|
"simple-git": "2.19.0",
|
||||||
"solr-node": "1.2.1",
|
"solr-node": "1.2.1",
|
||||||
"sqlite3": "5.0.0",
|
"sqlite3": "5.0.0",
|
||||||
"ssh2": "0.8.9",
|
"ssh2": "0.8.9",
|
||||||
"ssh2-promise": "0.1.7",
|
"ssh2-promise": "0.1.7",
|
||||||
"striptags": "3.1.1",
|
"striptags": "3.1.1",
|
||||||
"subscriptions-transport-ws": "0.9.17",
|
"subscriptions-transport-ws": "0.9.18",
|
||||||
"tar-fs": "2.1.0",
|
"tar-fs": "2.1.0",
|
||||||
"twemoji": "13.0.0",
|
"twemoji": "13.0.1",
|
||||||
"uslug": "1.0.4",
|
"uslug": "1.0.4",
|
||||||
"uuid": "8.2.0",
|
"uuid": "8.3.0",
|
||||||
"validate.js": "0.13.1",
|
"validate.js": "0.13.1",
|
||||||
"winston": "3.3.3",
|
"winston": "3.3.3",
|
||||||
"xss": "1.0.7",
|
"xss": "1.0.8",
|
||||||
"yargs": "15.4.1"
|
"yargs": "15.4.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "^7.10.4",
|
"@babel/cli": "^7.10.5",
|
||||||
"@babel/core": "^7.10.4",
|
"@babel/core": "^7.11.4",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.10.4",
|
"@babel/plugin-proposal-class-properties": "^7.10.4",
|
||||||
"@babel/plugin-proposal-decorators": "^7.10.4",
|
"@babel/plugin-proposal-decorators": "^7.10.5",
|
||||||
"@babel/plugin-proposal-export-namespace-from": "^7.10.4",
|
"@babel/plugin-proposal-export-namespace-from": "^7.10.4",
|
||||||
"@babel/plugin-proposal-function-sent": "^7.10.4",
|
"@babel/plugin-proposal-function-sent": "^7.10.4",
|
||||||
"@babel/plugin-proposal-json-strings": "^7.10.4",
|
"@babel/plugin-proposal-json-strings": "^7.10.4",
|
||||||
@ -198,11 +198,11 @@
|
|||||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||||
"@babel/plugin-syntax-import-meta": "^7.10.4",
|
"@babel/plugin-syntax-import-meta": "^7.10.4",
|
||||||
"@babel/polyfill": "^7.10.4",
|
"@babel/polyfill": "^7.10.4",
|
||||||
"@babel/preset-env": "^7.10.4",
|
"@babel/preset-env": "^7.11.0",
|
||||||
"@mdi/font": "5.3.45",
|
"@mdi/font": "5.5.55",
|
||||||
"@panter/vue-i18next": "0.15.2",
|
"@panter/vue-i18next": "0.15.2",
|
||||||
"@requarks/ckeditor5": "19.0.1-wiki.2",
|
"@requarks/ckeditor5": "19.0.1-wiki.2",
|
||||||
"@vue/babel-preset-app": "4.4.6",
|
"@vue/babel-preset-app": "4.5.4",
|
||||||
"animate-sass": "0.8.2",
|
"animate-sass": "0.8.2",
|
||||||
"animated-number-vue": "1.0.0",
|
"animated-number-vue": "1.0.0",
|
||||||
"apollo-cache-inmemory": "1.6.6",
|
"apollo-cache-inmemory": "1.6.6",
|
||||||
@ -214,11 +214,11 @@
|
|||||||
"apollo-link-persisted-queries": "0.2.2",
|
"apollo-link-persisted-queries": "0.2.2",
|
||||||
"apollo-link-ws": "1.0.20",
|
"apollo-link-ws": "1.0.20",
|
||||||
"apollo-utilities": "1.3.4",
|
"apollo-utilities": "1.3.4",
|
||||||
"autoprefixer": "9.8.5",
|
"autoprefixer": "9.8.6",
|
||||||
"babel-eslint": "10.1.0",
|
"babel-eslint": "10.1.0",
|
||||||
"babel-jest": "26.1.0",
|
"babel-jest": "26.3.0",
|
||||||
"babel-loader": "^8.1.0",
|
"babel-loader": "^8.1.0",
|
||||||
"babel-plugin-graphql-tag": "2.5.0",
|
"babel-plugin-graphql-tag": "3.0.0",
|
||||||
"babel-plugin-lodash": "3.3.4",
|
"babel-plugin-lodash": "3.3.4",
|
||||||
"babel-plugin-prismjs": "2.0.1",
|
"babel-plugin-prismjs": "2.0.1",
|
||||||
"babel-plugin-transform-imports": "2.0.0",
|
"babel-plugin-transform-imports": "2.0.0",
|
||||||
@ -228,16 +228,16 @@
|
|||||||
"chart.js": "2.9.3",
|
"chart.js": "2.9.3",
|
||||||
"clean-webpack-plugin": "3.0.0",
|
"clean-webpack-plugin": "3.0.0",
|
||||||
"clipboard": "2.0.6",
|
"clipboard": "2.0.6",
|
||||||
"codemirror": "5.55.0",
|
"codemirror": "5.57.0",
|
||||||
"copy-webpack-plugin": "6.0.3",
|
"copy-webpack-plugin": "6.0.3",
|
||||||
"core-js": "3.6.5",
|
"core-js": "3.6.5",
|
||||||
"css-loader": "3.6.0",
|
"css-loader": "4.2.1",
|
||||||
"cssnano": "4.1.10",
|
"cssnano": "4.1.10",
|
||||||
"cypress": "4.10.0",
|
"cypress": "5.0.0",
|
||||||
"d3": "5.16.0",
|
"d3": "5.16.0",
|
||||||
"duplicate-package-checker-webpack-plugin": "3.0.0",
|
"duplicate-package-checker-webpack-plugin": "3.0.0",
|
||||||
"epic-spinners": "1.1.0",
|
"epic-spinners": "1.1.0",
|
||||||
"eslint": "7.4.0",
|
"eslint": "7.7.0",
|
||||||
"eslint-config-requarks": "1.0.7",
|
"eslint-config-requarks": "1.0.7",
|
||||||
"eslint-config-standard": "14.1.1",
|
"eslint-config-standard": "14.1.1",
|
||||||
"eslint-plugin-cypress": "2.11.1",
|
"eslint-plugin-cypress": "2.11.1",
|
||||||
@ -248,11 +248,11 @@
|
|||||||
"eslint-plugin-vue": "6.2.2",
|
"eslint-plugin-vue": "6.2.2",
|
||||||
"fibers": "5.0.0",
|
"fibers": "5.0.0",
|
||||||
"file-loader": "6.0.0",
|
"file-loader": "6.0.0",
|
||||||
"filepond": "4.18.0",
|
"filepond": "4.19.2",
|
||||||
"filepond-plugin-file-validate-type": "1.2.5",
|
"filepond-plugin-file-validate-type": "1.2.5",
|
||||||
"filesize.js": "2.0.0",
|
"filesize.js": "2.0.0",
|
||||||
"graphql-persisted-document-loader": "2.0.0",
|
"graphql-persisted-document-loader": "2.0.0",
|
||||||
"graphql-tag": "^2.10.4",
|
"graphql-tag": "^2.11.0",
|
||||||
"hammerjs": "2.0.8",
|
"hammerjs": "2.0.8",
|
||||||
"html-webpack-plugin": "4.3.0",
|
"html-webpack-plugin": "4.3.0",
|
||||||
"html-webpack-pug-plugin": "2.0.0",
|
"html-webpack-pug-plugin": "2.0.0",
|
||||||
@ -260,11 +260,11 @@
|
|||||||
"i18next-localstorage-backend": "3.1.1",
|
"i18next-localstorage-backend": "3.1.1",
|
||||||
"i18next-xhr-backend": "3.2.2",
|
"i18next-xhr-backend": "3.2.2",
|
||||||
"ignore-loader": "0.1.2",
|
"ignore-loader": "0.1.2",
|
||||||
"jest": "26.1.0",
|
"jest": "26.4.2",
|
||||||
"js-beautify": "1.11.0",
|
"js-beautify": "1.13.0",
|
||||||
"js-cookie": "2.2.1",
|
"js-cookie": "2.2.1",
|
||||||
"mermaid": "8.5.2",
|
"mermaid": "8.7.0",
|
||||||
"mini-css-extract-plugin": "0.9.0",
|
"mini-css-extract-plugin": "0.10.0",
|
||||||
"moment-duration-format": "2.3.2",
|
"moment-duration-format": "2.3.2",
|
||||||
"moment-timezone-data-webpack-plugin": "1.3.0",
|
"moment-timezone-data-webpack-plugin": "1.3.0",
|
||||||
"offline-plugin": "5.0.7",
|
"offline-plugin": "5.0.7",
|
||||||
@ -277,52 +277,52 @@
|
|||||||
"postcss-loader": "3.0.0",
|
"postcss-loader": "3.0.0",
|
||||||
"postcss-preset-env": "6.7.0",
|
"postcss-preset-env": "6.7.0",
|
||||||
"postcss-selector-parser": "6.0.2",
|
"postcss-selector-parser": "6.0.2",
|
||||||
"prismjs": "1.20.0",
|
"prismjs": "1.21.0",
|
||||||
"pug-lint": "2.6.0",
|
"pug-lint": "2.6.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": "4.0.1",
|
"raw-loader": "4.0.1",
|
||||||
"resolve-url-loader": "3.1.1",
|
"resolve-url-loader": "3.1.1",
|
||||||
"sass": "1.26.10",
|
"sass": "1.26.10",
|
||||||
"sass-loader": "9.0.2",
|
"sass-loader": "9.0.3",
|
||||||
"sass-resources-loader": "2.0.3",
|
"sass-resources-loader": "2.1.0",
|
||||||
"script-ext-html-webpack-plugin": "2.1.4",
|
"script-ext-html-webpack-plugin": "2.1.4",
|
||||||
"simple-progress-webpack-plugin": "1.1.2",
|
"simple-progress-webpack-plugin": "1.1.2",
|
||||||
"style-loader": "1.2.1",
|
"style-loader": "1.2.1",
|
||||||
"terser": "4.8.0",
|
"terser": "5.2.1",
|
||||||
"twemoji-awesome": "1.0.6",
|
"twemoji-awesome": "1.0.6",
|
||||||
"url-loader": "4.1.0",
|
"url-loader": "4.1.0",
|
||||||
"velocity-animate": "1.5.2",
|
"velocity-animate": "1.5.2",
|
||||||
"viz.js": "2.1.2",
|
"viz.js": "2.1.2",
|
||||||
"vue": "2.6.11",
|
"vue": "2.6.12",
|
||||||
"vue-apollo": "3.0.3",
|
"vue-apollo": "3.0.4",
|
||||||
"vue-chartjs": "3.5.0",
|
"vue-chartjs": "3.5.1",
|
||||||
"vue-clipboards": "1.3.0",
|
"vue-clipboards": "1.3.0",
|
||||||
"vue-filepond": "6.0.2",
|
"vue-filepond": "6.0.2",
|
||||||
"vue-hot-reload-api": "2.3.4",
|
"vue-hot-reload-api": "2.3.4",
|
||||||
"vue-loader": "15.9.3",
|
"vue-loader": "15.9.3",
|
||||||
"vue-moment": "4.1.0",
|
"vue-moment": "4.1.0",
|
||||||
"vue-router": "3.3.4",
|
"vue-router": "3.4.3",
|
||||||
"vue-status-indicator": "1.2.1",
|
"vue-status-indicator": "1.2.1",
|
||||||
"vue-template-compiler": "2.6.11",
|
"vue-template-compiler": "2.6.12",
|
||||||
"vue2-animate": "2.1.3",
|
"vue2-animate": "2.1.3",
|
||||||
"vuedraggable": "2.24.0",
|
"vuedraggable": "2.24.1",
|
||||||
"vuescroll": "4.16.0",
|
"vuescroll": "4.16.1",
|
||||||
"vuetify": "2.3.4",
|
"vuetify": "2.3.9",
|
||||||
"vuetify-loader": "1.6.0",
|
"vuetify-loader": "1.6.0",
|
||||||
"vuex": "3.5.1",
|
"vuex": "3.5.1",
|
||||||
"vuex-pathify": "1.4.1",
|
"vuex-pathify": "1.4.1",
|
||||||
"vuex-persistedstate": "3.0.1",
|
"vuex-persistedstate": "3.1.0",
|
||||||
"webpack": "4.43.0",
|
"webpack": "4.44.1",
|
||||||
"webpack-bundle-analyzer": "3.8.0",
|
"webpack-bundle-analyzer": "3.8.0",
|
||||||
"webpack-cli": "3.3.12",
|
"webpack-cli": "3.3.12",
|
||||||
"webpack-dev-middleware": "3.7.2",
|
"webpack-dev-middleware": "3.7.2",
|
||||||
"webpack-hot-middleware": "2.25.0",
|
"webpack-hot-middleware": "2.25.0",
|
||||||
"webpack-merge": "5.0.9",
|
"webpack-merge": "5.1.2",
|
||||||
"webpack-modernizr-loader": "5.0.0",
|
"webpack-modernizr-loader": "5.0.0",
|
||||||
"webpack-subresource-integrity": "1.4.1",
|
"webpack-subresource-integrity": "1.4.1",
|
||||||
"webpackbar": "4.0.0",
|
"webpackbar": "4.0.0",
|
||||||
"whatwg-fetch": "3.2.0",
|
"whatwg-fetch": "3.4.0",
|
||||||
"write-file-webpack-plugin": "4.5.1",
|
"write-file-webpack-plugin": "4.5.1",
|
||||||
"xterm": "4.8.1",
|
"xterm": "4.8.1",
|
||||||
"zxcvbn": "4.4.2"
|
"zxcvbn": "4.4.2"
|
||||||
|
@ -44,6 +44,7 @@ type AuthenticationMutation {
|
|||||||
loginTFA(
|
loginTFA(
|
||||||
continuationToken: String!
|
continuationToken: String!
|
||||||
securityCode: String!
|
securityCode: String!
|
||||||
|
setup: Boolean
|
||||||
): AuthenticationLoginResponse @rateLimit(limit: 5, duration: 60)
|
): AuthenticationLoginResponse @rateLimit(limit: 5, duration: 60)
|
||||||
|
|
||||||
loginChangePassword(
|
loginChangePassword(
|
||||||
@ -108,8 +109,10 @@ type AuthenticationLoginResponse {
|
|||||||
jwt: String
|
jwt: String
|
||||||
mustChangePwd: Boolean
|
mustChangePwd: Boolean
|
||||||
mustProvideTFA: Boolean
|
mustProvideTFA: Boolean
|
||||||
|
mustSetupTFA: Boolean
|
||||||
continuationToken: String
|
continuationToken: String
|
||||||
redirect: String
|
redirect: String
|
||||||
|
tfaQRImage: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthenticationRegisterResponse {
|
type AuthenticationRegisterResponse {
|
||||||
|
@ -140,6 +140,7 @@ type User {
|
|||||||
createdAt: Date!
|
createdAt: Date!
|
||||||
updatedAt: Date!
|
updatedAt: Date!
|
||||||
lastLoginAt: Date
|
lastLoginAt: Date
|
||||||
|
tfaIsActive: Boolean!
|
||||||
groups: [Group]!
|
groups: [Group]!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ module.exports = class Asset extends Model {
|
|||||||
static async getAssetFromStorage(assetPath, res) {
|
static async getAssetFromStorage(assetPath, res) {
|
||||||
const localLocations = await WIKI.models.storage.getLocalLocations({
|
const localLocations = await WIKI.models.storage.getLocalLocations({
|
||||||
asset: {
|
asset: {
|
||||||
path: assetPath,
|
path: assetPath
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
for (let location of _.filter(localLocations, location => Boolean(location.path))) {
|
for (let location of _.filter(localLocations, location => Boolean(location.path))) {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
/* global WIKI */
|
/* global WIKI */
|
||||||
|
|
||||||
const Model = require('objection').Model
|
const Model = require('objection').Model
|
||||||
const moment = require('moment')
|
const { DateTime } = require('luxon')
|
||||||
const nanoid = require('nanoid').nanoid
|
const { nanoid } = require('nanoid')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Users model
|
* Users model
|
||||||
@ -41,25 +41,27 @@ module.exports = class UserKey extends Model {
|
|||||||
async $beforeInsert(context) {
|
async $beforeInsert(context) {
|
||||||
await super.$beforeInsert(context)
|
await super.$beforeInsert(context)
|
||||||
|
|
||||||
this.createdAt = moment.utc().toISOString()
|
this.createdAt = DateTime.utc().toISO()
|
||||||
}
|
}
|
||||||
|
|
||||||
static async generateToken ({ userId, kind }, context) {
|
static async generateToken ({ userId, kind }, context) {
|
||||||
const token = nanoid()
|
const token = await nanoid()
|
||||||
await WIKI.models.userKeys.query().insert({
|
await WIKI.models.userKeys.query().insert({
|
||||||
kind,
|
kind,
|
||||||
token,
|
token,
|
||||||
validUntil: moment.utc().add(1, 'days').toISOString(),
|
validUntil: DateTime.utc().plus({ days: 1 }).toISO(),
|
||||||
userId
|
userId
|
||||||
})
|
})
|
||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
|
|
||||||
static async validateToken ({ kind, token }, context) {
|
static async validateToken ({ kind, token, skipDelete }, context) {
|
||||||
const res = await WIKI.models.userKeys.query().findOne({ kind, token }).withGraphJoined('user')
|
const res = await WIKI.models.userKeys.query().findOne({ kind, token }).withGraphJoined('user')
|
||||||
if (res) {
|
if (res) {
|
||||||
await WIKI.models.userKeys.query().deleteById(res.id)
|
if (skipDelete !== true) {
|
||||||
if (moment.utc().isAfter(moment.utc(res.validUntil))) {
|
await WIKI.models.userKeys.query().deleteById(res.id)
|
||||||
|
}
|
||||||
|
if (DateTime.utc() > DateTime.fromISO(res.validUntil)) {
|
||||||
throw new WIKI.Error.AuthValidationTokenInvalid()
|
throw new WIKI.Error.AuthValidationTokenInvalid()
|
||||||
}
|
}
|
||||||
return res.user
|
return res.user
|
||||||
@ -67,4 +69,8 @@ module.exports = class UserKey extends Model {
|
|||||||
throw new WIKI.Error.AuthValidationTokenInvalid()
|
throw new WIKI.Error.AuthValidationTokenInvalid()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async destroyToken ({ token }) {
|
||||||
|
return WIKI.models.userKeys.query().findOne({ token }).delete()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ const tfa = require('node-2fa')
|
|||||||
const jwt = require('jsonwebtoken')
|
const jwt = require('jsonwebtoken')
|
||||||
const Model = require('objection').Model
|
const Model = require('objection').Model
|
||||||
const validate = require('validate.js')
|
const validate = require('validate.js')
|
||||||
|
const qr = require('qr-image')
|
||||||
|
|
||||||
const bcryptRegexp = /^\$2[ayb]\$[0-9]{2}\$[A-Za-z0-9./]{53}$/
|
const bcryptRegexp = /^\$2[ayb]\$[0-9]{2}\$[A-Za-z0-9./]{53}$/
|
||||||
|
|
||||||
@ -118,14 +119,22 @@ module.exports = class User extends Model {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async enableTFA() {
|
async generateTFA() {
|
||||||
let tfaInfo = tfa.generateSecret({
|
let tfaInfo = tfa.generateSecret({
|
||||||
name: WIKI.config.site.title
|
name: WIKI.config.title,
|
||||||
|
account: this.email
|
||||||
})
|
})
|
||||||
return this.$query.patch({
|
await WIKI.models.users.query().findById(this.id).patch({
|
||||||
tfaIsActive: true,
|
tfaIsActive: false,
|
||||||
tfaSecret: tfaInfo.secret
|
tfaSecret: tfaInfo.secret
|
||||||
})
|
})
|
||||||
|
return qr.imageSync(`otpauth://totp/${WIKI.config.title}:${this.email}?secret=${tfaInfo.secret}`, { type: 'svg' })
|
||||||
|
}
|
||||||
|
|
||||||
|
async enableTFA() {
|
||||||
|
return WIKI.models.users.query().findById(this.id).patch({
|
||||||
|
tfaIsActive: true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async disableTFA() {
|
async disableTFA() {
|
||||||
@ -135,7 +144,7 @@ module.exports = class User extends Model {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async verifyTFA(code) {
|
verifyTFA(code) {
|
||||||
let result = tfa.verifyToken(this.tfaSecret, code)
|
let result = tfa.verifyToken(this.tfaSecret, code)
|
||||||
return (result && _.has(result, 'delta') && result.delta === 0)
|
return (result && _.has(result, 'delta') && result.delta === 0)
|
||||||
}
|
}
|
||||||
@ -281,55 +290,12 @@ module.exports = class User extends Model {
|
|||||||
if (err) { return reject(err) }
|
if (err) { return reject(err) }
|
||||||
if (!user) { return reject(new WIKI.Error.AuthLoginFailed()) }
|
if (!user) { return reject(new WIKI.Error.AuthLoginFailed()) }
|
||||||
|
|
||||||
// Get redirect target
|
try {
|
||||||
user.groups = await user.$relatedQuery('groups').select('groups.id', 'permissions', 'redirectOnLogin')
|
const resp = await WIKI.models.users.afterLoginChecks(user, context)
|
||||||
let redirect = '/'
|
resolve(resp)
|
||||||
if (user.groups && user.groups.length > 0) {
|
} catch (err) {
|
||||||
redirect = user.groups[0].redirectOnLogin
|
reject(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must Change Password?
|
|
||||||
if (user.mustChangePwd) {
|
|
||||||
try {
|
|
||||||
const pwdChangeToken = await WIKI.models.userKeys.generateToken({
|
|
||||||
kind: 'changePwd',
|
|
||||||
userId: user.id
|
|
||||||
})
|
|
||||||
|
|
||||||
return resolve({
|
|
||||||
mustChangePwd: true,
|
|
||||||
continuationToken: pwdChangeToken,
|
|
||||||
redirect
|
|
||||||
})
|
|
||||||
} catch (errc) {
|
|
||||||
WIKI.logger.warn(errc)
|
|
||||||
return reject(new WIKI.Error.AuthGenericError())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is 2FA required?
|
|
||||||
if (user.tfaIsActive) {
|
|
||||||
try {
|
|
||||||
const tfaToken = await WIKI.models.userKeys.generateToken({
|
|
||||||
kind: 'tfa',
|
|
||||||
userId: user.id
|
|
||||||
})
|
|
||||||
return resolve({
|
|
||||||
tfaRequired: true,
|
|
||||||
continuationToken: tfaToken,
|
|
||||||
redirect
|
|
||||||
})
|
|
||||||
} catch (errc) {
|
|
||||||
WIKI.logger.warn(errc)
|
|
||||||
return reject(new WIKI.Error.AuthGenericError())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context.req.logIn(user, { session: !strInfo.useForm }, async errc => {
|
|
||||||
if (errc) { return reject(errc) }
|
|
||||||
const jwtToken = await WIKI.models.users.refreshToken(user)
|
|
||||||
resolve({ jwt: jwtToken.token, redirect })
|
|
||||||
})
|
|
||||||
})(context.req, context.res, () => {})
|
})(context.req, context.res, () => {})
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@ -337,6 +303,79 @@ module.exports = class User extends Model {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async afterLoginChecks (user, context, { skipTFA, skipChangePwd } = { skipTFA: false, skipChangePwd: false }) {
|
||||||
|
// Get redirect target
|
||||||
|
user.groups = await user.$relatedQuery('groups').select('groups.id', 'permissions', 'redirectOnLogin')
|
||||||
|
let redirect = '/'
|
||||||
|
if (user.groups && user.groups.length > 0) {
|
||||||
|
redirect = user.groups[0].redirectOnLogin
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is 2FA required?
|
||||||
|
if (!skipTFA) {
|
||||||
|
if (user.tfaIsActive && user.tfaSecret) {
|
||||||
|
try {
|
||||||
|
const tfaToken = await WIKI.models.userKeys.generateToken({
|
||||||
|
kind: 'tfa',
|
||||||
|
userId: user.id
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
mustProvideTFA: true,
|
||||||
|
continuationToken: tfaToken,
|
||||||
|
redirect
|
||||||
|
}
|
||||||
|
} catch (errc) {
|
||||||
|
WIKI.logger.warn(errc)
|
||||||
|
throw new WIKI.Error.AuthGenericError()
|
||||||
|
}
|
||||||
|
} else if (WIKI.config.auth.enforce2FA || (user.tfaIsActive && !user.tfaSecret)) {
|
||||||
|
try {
|
||||||
|
const tfaQRImage = await user.generateTFA()
|
||||||
|
const tfaToken = await WIKI.models.userKeys.generateToken({
|
||||||
|
kind: 'tfaSetup',
|
||||||
|
userId: user.id
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
mustSetupTFA: true,
|
||||||
|
continuationToken: tfaToken,
|
||||||
|
tfaQRImage,
|
||||||
|
redirect
|
||||||
|
}
|
||||||
|
} catch (errc) {
|
||||||
|
WIKI.logger.warn(errc)
|
||||||
|
throw new WIKI.Error.AuthGenericError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must Change Password?
|
||||||
|
if (!skipChangePwd && user.mustChangePwd) {
|
||||||
|
try {
|
||||||
|
const pwdChangeToken = await WIKI.models.userKeys.generateToken({
|
||||||
|
kind: 'changePwd',
|
||||||
|
userId: user.id
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
mustChangePwd: true,
|
||||||
|
continuationToken: pwdChangeToken,
|
||||||
|
redirect
|
||||||
|
}
|
||||||
|
} catch (errc) {
|
||||||
|
WIKI.logger.warn(errc)
|
||||||
|
throw new WIKI.Error.AuthGenericError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
context.req.login(user, { session: false }, async errc => {
|
||||||
|
if (errc) { return reject(errc) }
|
||||||
|
const jwtToken = await WIKI.models.users.refreshToken(user)
|
||||||
|
resolve({ jwt: jwtToken.token, redirect })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
static async refreshToken(user) {
|
static async refreshToken(user) {
|
||||||
if (_.isSafeInteger(user)) {
|
if (_.isSafeInteger(user)) {
|
||||||
user = await WIKI.models.users.query().findById(user).withGraphFetched('groups').modifyGraph('groups', builder => {
|
user = await WIKI.models.users.query().findById(user).withGraphFetched('groups').modifyGraph('groups', builder => {
|
||||||
@ -384,26 +423,21 @@ module.exports = class User extends Model {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async loginTFA (opts, context) {
|
static async loginTFA ({ securityCode, continuationToken, setup }, context) {
|
||||||
if (opts.securityCode.length === 6 && opts.loginToken.length === 64) {
|
if (securityCode.length === 6 && continuationToken.length > 1) {
|
||||||
let result = await WIKI.redis.get(`tfa:${opts.loginToken}`)
|
const user = await WIKI.models.userKeys.validateToken({
|
||||||
if (result) {
|
kind: setup ? 'tfaSetup' : 'tfa',
|
||||||
let userId = _.toSafeInteger(result)
|
token: continuationToken,
|
||||||
if (userId && userId > 0) {
|
skipDelete: setup
|
||||||
let user = await WIKI.models.users.query().findById(userId)
|
})
|
||||||
if (user && user.verifyTFA(opts.securityCode)) {
|
if (user) {
|
||||||
return Promise.fromCallback(clb => {
|
if (user.verifyTFA(securityCode)) {
|
||||||
context.req.logIn(user, clb)
|
if (setup) {
|
||||||
}).return({
|
await user.enableTFA()
|
||||||
succeeded: true,
|
|
||||||
message: 'Login Successful'
|
|
||||||
}).catch(err => {
|
|
||||||
WIKI.logger.warn(err)
|
|
||||||
throw new WIKI.Error.AuthGenericError()
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
throw new WIKI.Error.AuthTFAFailed()
|
|
||||||
}
|
}
|
||||||
|
return WIKI.models.users.afterLoginChecks(user, context, { skipTFA: true })
|
||||||
|
} else {
|
||||||
|
throw new WIKI.Error.AuthTFAFailed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -442,7 +442,7 @@ module.exports = () => {
|
|||||||
WIKI.logger.info('HTTP Server: [ RUNNING ]')
|
WIKI.logger.info('HTTP Server: [ RUNNING ]')
|
||||||
WIKI.logger.info('🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻')
|
WIKI.logger.info('🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻')
|
||||||
WIKI.logger.info('')
|
WIKI.logger.info('')
|
||||||
WIKI.logger.info(`Browse to http://localhost:${WIKI.config.port}/ to complete setup!`)
|
WIKI.logger.info(`Browse to http://YOUR-SERVER-IP:${WIKI.config.port}/ to complete setup!`)
|
||||||
WIKI.logger.info('')
|
WIKI.logger.info('')
|
||||||
WIKI.logger.info('🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺')
|
WIKI.logger.info('🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺')
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user