123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662 |
- <template lang='pug'>
- q-page.admin-mail
- .row.q-pa-md.items-center
- .col-auto
- img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-security-lock.svg')
- .col.q-pl-md
- .text-h5.text-primary.animated.fadeInLeft {{ t('admin.auth.title') }}
- .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ t('admin.auth.subtitle') }}
- .col-auto
- q-btn.q-mr-sm.acrylic-btn(
- icon='las la-question-circle'
- flat
- color='grey'
- :href='siteStore.docsBase + `/admin/auth`'
- target='_blank'
- type='a'
- )
- q-btn(
- unelevated
- icon='mdi-check'
- :label='t(`common.actions.apply`)'
- color='secondary'
- @click='save'
- :loading='state.loading > 0'
- )
- q-separator(inset)
- .row.q-pa-md.q-col-gutter-md
- .col-auto
- q-card.rounded-borders.bg-dark
- q-list(
- style='min-width: 350px;'
- padding
- dark
- )
- q-item(
- v-for='str of combinedActiveStrategies'
- :key='str.key'
- active-class='bg-primary text-white'
- :active='state.selectedStrategy === str.id'
- @click='state.selectedStrategy = str.id'
- clickable
- )
- q-item-section(side)
- q-icon(:name='`img:` + str.strategy.icon')
- q-item-section
- q-item-label {{str.displayName}}
- q-item-label(caption) {{str.strategy.title}}
- q-item-section(side)
- status-light(:color='str.isEnabled ? `positive` : `negative`', :pulse='str.isEnabled')
- q-btn.q-mt-sm.full-width(
- color='primary'
- icon='las la-plus'
- :label='t(`admin.auth.addStrategy`)'
- )
- q-menu(auto-close, fit, max-width='300px')
- q-list(separator)
- q-item(
- v-for='str of availableStrategies'
- :key='str.key'
- clickable
- @click='addStrategy(str)'
- )
- q-item-section(avatar)
- q-avatar(
- rounded
- color='dark'
- text-color='white'
- )
- q-icon(
- :name='`img:` + str.icon'
- )
- q-item-section
- q-item-label: strong {{str.title}}
- q-item-label(caption, lines='2') {{str.description}}
- .col
- q-card.shadow-1.q-pb-sm
- q-card-section
- .text-subtitle1 {{t('admin.storage.contentTypes')}}
- .text-body2.text-grey {{ t('admin.storage.contentTypesHint') }}
- //- -----------------------
- //- Configuration
- //- -----------------------
- q-card.shadow-1.q-pb-sm.q-mt-md
- q-card-section
- .text-subtitle1 {{t('admin.storage.config')}}
- q-banner.q-mt-md(
- v-if='!state.strategy.config || Object.keys(state.strategy.config).length < 1'
- rounded
- :class='$q.dark.isActive ? `bg-negative text-white` : `bg-grey-2 text-grey-7`'
- ) {{t('admin.storage.noConfigOption')}}
- template(
- v-for='(cfg, cfgKey, idx) in state.strategy.config'
- )
- template(
- v-if='configIfCheck(cfg.if)'
- )
- q-separator.q-my-sm(inset, v-if='idx > 0')
- q-item(v-if='cfg.type === `boolean`', tag='label')
- blueprint-icon(:icon='cfg.icon', :hue-rotate='cfg.readOnly ? -45 : 0')
- q-item-section
- q-item-label {{cfg.title}}
- q-item-label(caption) {{cfg.hint}}
- q-item-section(avatar)
- q-toggle(
- v-model='cfg.value'
- color='primary'
- checked-icon='las la-check'
- unchecked-icon='las la-times'
- :aria-label='t(`admin.general.allowComments`)'
- :disable='cfg.readOnly'
- )
- q-item(v-else)
- blueprint-icon(:icon='cfg.icon', :hue-rotate='cfg.readOnly ? -45 : 0')
- q-item-section
- q-item-label {{cfg.title}}
- q-item-label(caption) {{cfg.hint}}
- q-item-section(
- :style='cfg.type === `number` ? `flex: 0 0 150px;` : ``'
- :class='{ "col-auto": cfg.enum && cfg.enumDisplay === `buttons` }'
- )
- q-btn-toggle(
- v-if='cfg.enum && cfg.enumDisplay === `buttons`'
- v-model='cfg.value'
- push
- glossy
- no-caps
- toggle-color='primary'
- :options='cfg.enum'
- :disable='cfg.readOnly'
- )
- q-select(
- v-else-if='cfg.enum'
- outlined
- v-model='cfg.value'
- :options='cfg.enum'
- emit-value
- map-options
- dense
- options-dense
- :aria-label='cfg.title'
- :disable='cfg.readOnly'
- )
- q-input(
- v-else
- outlined
- v-model='cfg.value'
- dense
- :type='cfg.multiline ? `textarea` : `input`'
- :aria-label='cfg.title'
- :disable='cfg.readOnly'
- )
- .col-auto(v-if='state.selectedStrategy && state.strategy')
- //- -----------------------
- //- Infobox
- //- -----------------------
- q-card.rounded-borders.q-pb-md(style='width: 350px;')
- q-card-section
- .text-subtitle1 {{state.strategy.strategy.title}}
- q-img.q-mt-sm.rounded-borders(
- :src='state.strategy.strategy.logo'
- fit='contain'
- no-spinner
- style='height: 100px;'
- )
- .text-body2.q-mt-md {{state.strategy.strategy.description}}
- q-separator.q-mb-sm(inset)
- q-item
- q-item-section
- q-item-label.text-grey {{t(`admin.auth.vendor`)}}
- q-item-label {{state.strategy.strategy.vendor}}
- q-separator.q-my-sm(inset)
- q-item
- q-item-section
- q-item-label.text-grey {{t(`admin.auth.vendorWebsite`)}}
- q-item-label: a(:href='state.strategy.strategy.website', target='_blank', rel='noreferrer') {{state.strategy.strategy.website}}
- //- -----------------------
- //- Status
- //- -----------------------
- q-card.rounded-borders.q-pb-md.q-mt-md(style='width: 350px;')
- q-card-section
- .text-subtitle1 {{t(`admin.auth.status`)}}
- q-item(tag='label')
- q-item-section
- q-item-label {{t(`admin.auth.enabled`)}}
- q-item-label(caption) {{t(`admin.auth.enabledHint`)}}
- q-item-label.text-deep-orange(v-if='state.strategy.strategy.key === `local`', caption) {{t(`admin.auth.enabledForced`)}}
- q-item-section(avatar)
- q-toggle(
- v-model='state.strategy.isEnabled'
- :disable='state.strategy.strategy.key === `local`'
- color='primary'
- checked-icon='las la-check'
- unchecked-icon='las la-times'
- :aria-label='t(`admin.auth.enabled`)'
- )
- q-separator.q-my-sm(inset)
- q-item
- q-item-section
- q-btn.acrylic-btn(
- icon='las la-trash-alt'
- flat
- color='negative'
- :disable='state.strategy.strategy.key === `local`'
- label='Delete Strategy'
- )
- //- v-flex(xs12, lg9)
- //- v-card.animated.fadeInUp.wait-p2s
- //- v-toolbar(color='primary', dense, flat, dark)
- //- .subtitle-1 {{strategy.displayName}} #[em ({{strategy.strategy.title}})]
- //- v-spacer
- //- 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
- //- .admin-providerlogo
- //- img(:src='strategy.strategy.logo', :alt='strategy.strategy.title')
- //- v-card-text
- //- .row
- //- .col-8
- //- v-text-field(
- //- outlined
- //- :label='$t(`admin.auth.displayName`)'
- //- v-model='strategy.displayName'
- //- prepend-icon='mdi-format-title'
- //- :hint='$t(`admin.auth.displayNameHint`)'
- //- persistent-hint
- //- )
- //- .col-4
- //- v-switch.mt-1(
- //- :label='$t(`admin.auth.strategyIsEnabled`)'
- //- v-model='strategy.isEnabled'
- //- color='primary'
- //- prepend-icon='mdi-power'
- //- :hint='$t(`admin.auth.strategyIsEnabledHint`)'
- //- persistent-hint
- //- inset
- //- :disabled='strategy.key === `local`'
- //- )
- //- template(v-if='strategy.config && Object.keys(strategy.config).length > 0')
- //- v-divider
- //- .overline.my-5 {{$t('admin.auth.strategyConfiguration')}}
- //- .pr-3
- //- 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
- //- .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-5(
- //- :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
- //- )
- //- v-card.mt-4.wiki-form.animated.fadeInUp.wait-p4s(v-if='selectedStrategy !== `local`')
- //- v-toolbar(color='primary', dense, flat, dark)
- //- .subtitle-1 {{$t('admin.auth.configReference')}}
- //- v-card-text
- //- .body-2 {{$t('admin.auth.configReferenceSubtitle')}}
- //- v-alert.mt-3.radius-7(v-if='host.length < 8', color='red', outlined, :value='true', icon='mdi-alert')
- //- i18next(path='admin.auth.siteUrlNotSetup', tag='span')
- //- strong(place='siteUrl') {{$t('admin.general.siteUrl')}}
- //- strong(place='general') {{$t('admin.general.title')}}
- //- .pa-3.mt-3.radius-7.grey(v-else, :class='$vuetify.theme.dark ? `darken-3-d5` : `lighten-3`')
- //- .body-2: strong {{$t('admin.auth.allowedWebOrigins')}}
- //- .body-2 {{host}}
- //- v-divider.my-3
- //- .body-2: strong {{$t('admin.auth.callbackUrl')}}
- //- .body-2 {{host}}/login/{{strategy.key}}/callback
- //- v-divider.my-3
- //- .body-2: strong {{$t('admin.auth.loginUrl')}}
- //- .body-2 {{host}}/login
- //- v-divider.my-3
- //- .body-2: strong {{$t('admin.auth.logoutUrl')}}
- //- .body-2 {{host}}
- //- v-divider.my-3
- //- .body-2: strong {{$t('admin.auth.tokenEndpointAuthMethod')}}
- //- .body-2 HTTP-POST
- </template>
- <script setup>
- import gql from 'graphql-tag'
- import { find, reject, transform } from 'lodash-es'
- import { v4 as uuid } from 'uuid'
- import { useI18n } from 'vue-i18n'
- import { useMeta, useQuasar } from 'quasar'
- import { computed, onMounted, reactive, watch, nextTick } from 'vue'
- import { useAdminStore } from 'src/stores/admin'
- import { useSiteStore } from 'src/stores/site'
- // QUASAR
- const $q = useQuasar()
- // STORES
- const adminStore = useAdminStore()
- const siteStore = useSiteStore()
- // I18N
- const { t } = useI18n()
- // META
- useMeta({
- title: t('admin.auth.title')
- })
- // DATA
- const state = reactive({
- loading: 0,
- groups: [],
- strategies: [],
- activeStrategies: [],
- selectedStrategy: '',
- host: '',
- strategy: {
- strategy: {}
- }
- })
- // COMPUTED
- const availableStrategies = computed(() => {
- return state.strategies.filter(str => str.key !== 'local')
- })
- const combinedActiveStrategies = computed(() => {
- return state.activeStrategies.map(str => ({
- ...str,
- strategy: find(state.strategies, ['key', str.strategy?.key]) || {}
- }))
- })
- // WATCHERS
- watch(() => state.selectedStrategy, (newValue, oldValue) => {
- const str = find(combinedActiveStrategies.value, ['id', newValue]) || {}
- str.config = transform(str.strategy.props, (cfg, v, k) => {
- cfg[k] = {
- ...v,
- value: str.config[k]
- }
- }, {})
- state.strategy = str
- })
- watch(() => state.activeStrategies, (newValue, oldValue) => {
- state.selectedStrategy = newValue[0]?.id
- })
- // METHODS
- async function load () {
- state.loading++
- $q.loading.show()
- const resp = await APOLLO_CLIENT.query({
- query: gql`
- query adminFetchAuthStrategies {
- authStrategies {
- key
- props
- title
- description
- isAvailable
- useForm
- usernameType
- logo
- color
- vendor
- website
- icon
- }
- authActiveStrategies {
- id
- strategy {
- key
- }
- displayName
- isEnabled
- config
- selfRegistration
- domainWhitelist
- autoEnrollGroups
- }
- }
- `,
- fetchPolicy: 'network-only'
- })
- state.strategies = resp?.data?.authStrategies || []
- state.activeStrategies = resp?.data?.authActiveStrategies || []
- $q.loading.hide()
- state.loading--
- }
- function configIfCheck (ifs) {
- if (!ifs || ifs.length < 1) { return true }
- return ifs.every(s => state.strategy.config[s.key]?.value === s.eq)
- }
- function addStrategy (str) {
- const newStr = {
- id: uuid(),
- strategy: {
- key: str.key
- },
- config: transform(str.props, (cfg, v, k) => {
- cfg[k] = v.default
- }, {}),
- isEnabled: false,
- displayName: str.title,
- selfRegistration: false,
- domainWhitelist: [],
- autoEnrollGroups: []
- }
- state.activeStrategies = [...state.activeStrategies, newStr]
- nextTick(() => {
- state.selectedStrategy = newStr.id
- })
- }
- function deleteStrategy (id) {
- state.activeStrategies = reject(state.activeStrategies, ['id', id])
- }
- async function save () {
- state.loading++
- try {
- const resp = await APOLLO_CLIENT.mutate({
- mutation: gql`
- mutation($strategies: [AuthenticationStrategyInput]!) {
- authentication {
- updateStrategies(strategies: $strategies) {
- responseResult {
- succeeded
- errorCode
- slug
- message
- }
- }
- }
- }
- `,
- variables: {
- strategies: this.activeStrategies.map((str, idx) => ({
- key: str.key,
- strategyKey: str.strategy.key,
- displayName: str.displayName,
- order: idx,
- isEnabled: str.isEnabled,
- config: str.config.map(cfg => ({ ...cfg, value: JSON.stringify({ v: cfg.value.value }) })),
- selfRegistration: str.selfRegistration,
- domainWhitelist: str.domainWhitelist,
- autoEnrollGroups: str.autoEnrollGroups
- }))
- }
- })
- if (resp?.data?.authentication?.updateStrategies?.operation.succeeded) {
- $q.notify({
- type: 'positive',
- message: t('admin.auth.saveSuccess')
- })
- } else {
- throw new Error(resp?.data?.authentication?.updateStrategies?.operation?.message || t('common.error.unexpected'))
- }
- } catch (err) {
- $q.notify({
- type: 'negative',
- message: 'Failed to save site theme config',
- caption: err.message
- })
- }
- state.loading--
- }
- // apollo: {
- // strategies: {
- // query: gql`
- // query {
- // authentication {
- // strategies {
- // key
- // title
- // description
- // isAvailable
- // useForm
- // logo
- // website
- // props {
- // key
- // value
- // }
- // }
- // }
- // }
- // `,
- // skip: true,
- // fetchPolicy: 'network-only',
- // 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
- // isEnabled
- // displayName
- // selfRegistration
- // domainWhitelist
- // autoEnrollGroups
- // }
- // }
- // }
- // `,
- // skip: true,
- // fetchPolicy: 'network-only',
- // update: (data) => _.sortBy(_.get(data, 'authentication.activeStrategies', []).map(str => ({
- // ...str,
- // config: _.sortBy(str.config.map(cfg => ({
- // ...cfg,
- // value: JSON.parse(cfg.value)
- // })), [t => t.value.order])
- // })), ['order']),
- // watchLoading (isLoading) {
- // this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-auth-activestrategies-refresh')
- // }
- // },
- // groups: {
- // query: gql`{ test }`,
- // fetchPolicy: 'network-only',
- // update: (data) => data.groups.list,
- // watchLoading (isLoading) {
- // this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-auth-groups-refresh')
- // }
- // },
- // host: {
- // query: gql`{ test }`,
- // fetchPolicy: 'network-only',
- // update: (data) => _.cloneDeep(data.site.config.host),
- // watchLoading (isLoading) {
- // this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-auth-host-refresh')
- // }
- // }
- onMounted(() => {
- load()
- })
- </script>
|