|
@@ -4,8 +4,8 @@ q-page.admin-mail
|
|
.col-auto
|
|
.col-auto
|
|
img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-protect.svg')
|
|
img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-protect.svg')
|
|
.col.q-pl-md
|
|
.col.q-pl-md
|
|
- .text-h5.text-primary.animated.fadeInLeft {{ $t('admin.security.title') }}
|
|
|
|
- .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.security.subtitle') }}
|
|
|
|
|
|
+ .text-h5.text-primary.animated.fadeInLeft {{ t('admin.security.title') }}
|
|
|
|
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ t('admin.security.subtitle') }}
|
|
.col-auto
|
|
.col-auto
|
|
q-btn.q-mr-sm.acrylic-btn(
|
|
q-btn.q-mr-sm.acrylic-btn(
|
|
icon='las la-question-circle'
|
|
icon='las la-question-circle'
|
|
@@ -19,16 +19,16 @@ q-page.admin-mail
|
|
icon='las la-redo-alt'
|
|
icon='las la-redo-alt'
|
|
flat
|
|
flat
|
|
color='secondary'
|
|
color='secondary'
|
|
- :loading='loading > 0'
|
|
|
|
|
|
+ :loading='state.loading > 0'
|
|
@click='load'
|
|
@click='load'
|
|
)
|
|
)
|
|
q-btn(
|
|
q-btn(
|
|
unelevated
|
|
unelevated
|
|
- icon='mdi-check'
|
|
|
|
- :label='$t(`common.actions.apply`)'
|
|
|
|
|
|
+ icon='fa-solid fa-check'
|
|
|
|
+ :label='t(`common.actions.apply`)'
|
|
color='secondary'
|
|
color='secondary'
|
|
@click='save'
|
|
@click='save'
|
|
- :loading='loading > 0'
|
|
|
|
|
|
+ :loading='state.loading > 0'
|
|
)
|
|
)
|
|
q-separator(inset)
|
|
q-separator(inset)
|
|
.row.q-pa-md.q-col-gutter-md
|
|
.row.q-pa-md.q-col-gutter-md
|
|
@@ -38,134 +38,134 @@ q-page.admin-mail
|
|
//- -----------------------
|
|
//- -----------------------
|
|
q-card.shadow-1.q-pb-sm
|
|
q-card.shadow-1.q-pb-sm
|
|
q-card-section
|
|
q-card-section
|
|
- .text-subtitle1 {{$t('admin.security.title')}}
|
|
|
|
|
|
+ .text-subtitle1 {{t('admin.security.title')}}
|
|
q-item.q-pt-none
|
|
q-item.q-pt-none
|
|
q-item-section
|
|
q-item-section
|
|
q-card.bg-negative.text-white.rounded-borders(flat)
|
|
q-card.bg-negative.text-white.rounded-borders(flat)
|
|
q-card-section.items-center(horizontal)
|
|
q-card-section.items-center(horizontal)
|
|
q-card-section.col-auto.q-pr-none
|
|
q-card-section.col-auto.q-pr-none
|
|
q-icon(name='las la-exclamation-triangle', size='sm')
|
|
q-icon(name='las la-exclamation-triangle', size='sm')
|
|
- q-card-section.text-caption {{ $t('admin.security.warn') }}
|
|
|
|
|
|
+ q-card-section.text-caption {{ t('admin.security.warn') }}
|
|
q-item(tag='label', v-ripple)
|
|
q-item(tag='label', v-ripple)
|
|
blueprint-icon(icon='rfid-signal')
|
|
blueprint-icon(icon='rfid-signal')
|
|
q-item-section
|
|
q-item-section
|
|
- q-item-label {{$t(`admin.security.disallowFloc`)}}
|
|
|
|
- q-item-label(caption) {{$t(`admin.security.disallowFlocHint`)}}
|
|
|
|
|
|
+ q-item-label {{t(`admin.security.disallowFloc`)}}
|
|
|
|
+ q-item-label(caption) {{t(`admin.security.disallowFlocHint`)}}
|
|
q-item-section(avatar)
|
|
q-item-section(avatar)
|
|
q-toggle(
|
|
q-toggle(
|
|
- v-model='config.disallowFloc'
|
|
|
|
|
|
+ v-model='state.config.disallowFloc'
|
|
color='primary'
|
|
color='primary'
|
|
checked-icon='las la-check'
|
|
checked-icon='las la-check'
|
|
unchecked-icon='las la-times'
|
|
unchecked-icon='las la-times'
|
|
- :aria-label='$t(`admin.security.disallowFloc`)'
|
|
|
|
|
|
+ :aria-label='t(`admin.security.disallowFloc`)'
|
|
)
|
|
)
|
|
q-separator.q-my-sm(inset)
|
|
q-separator.q-my-sm(inset)
|
|
q-item(tag='label', v-ripple)
|
|
q-item(tag='label', v-ripple)
|
|
blueprint-icon(icon='maximize-window')
|
|
blueprint-icon(icon='maximize-window')
|
|
q-item-section
|
|
q-item-section
|
|
- q-item-label {{$t(`admin.security.disallowIframe`)}}
|
|
|
|
- q-item-label(caption) {{$t(`admin.security.disallowIframeHint`)}}
|
|
|
|
|
|
+ q-item-label {{t(`admin.security.disallowIframe`)}}
|
|
|
|
+ q-item-label(caption) {{t(`admin.security.disallowIframeHint`)}}
|
|
q-item-section(avatar)
|
|
q-item-section(avatar)
|
|
q-toggle(
|
|
q-toggle(
|
|
- v-model='config.disallowIframe'
|
|
|
|
|
|
+ v-model='state.config.disallowIframe'
|
|
color='primary'
|
|
color='primary'
|
|
checked-icon='las la-check'
|
|
checked-icon='las la-check'
|
|
unchecked-icon='las la-times'
|
|
unchecked-icon='las la-times'
|
|
- :aria-label='$t(`admin.security.disallowIframe`)'
|
|
|
|
|
|
+ :aria-label='t(`admin.security.disallowIframe`)'
|
|
)
|
|
)
|
|
q-separator.q-my-sm(inset)
|
|
q-separator.q-my-sm(inset)
|
|
q-item(tag='label', v-ripple)
|
|
q-item(tag='label', v-ripple)
|
|
blueprint-icon(icon='do-not-touch')
|
|
blueprint-icon(icon='do-not-touch')
|
|
q-item-section
|
|
q-item-section
|
|
- q-item-label {{$t(`admin.security.enforceSameOriginReferrerPolicy`)}}
|
|
|
|
- q-item-label(caption) {{$t(`admin.security.enforceSameOriginReferrerPolicyHint`)}}
|
|
|
|
|
|
+ q-item-label {{t(`admin.security.enforceSameOriginReferrerPolicy`)}}
|
|
|
|
+ q-item-label(caption) {{t(`admin.security.enforceSameOriginReferrerPolicyHint`)}}
|
|
q-item-section(avatar)
|
|
q-item-section(avatar)
|
|
q-toggle(
|
|
q-toggle(
|
|
- v-model='config.enforceSameOriginReferrerPolicy'
|
|
|
|
|
|
+ v-model='state.config.enforceSameOriginReferrerPolicy'
|
|
color='primary'
|
|
color='primary'
|
|
checked-icon='las la-check'
|
|
checked-icon='las la-check'
|
|
unchecked-icon='las la-times'
|
|
unchecked-icon='las la-times'
|
|
- :aria-label='$t(`admin.security.enforceSameOriginReferrerPolicy`)'
|
|
|
|
|
|
+ :aria-label='t(`admin.security.enforceSameOriginReferrerPolicy`)'
|
|
)
|
|
)
|
|
q-separator.q-my-sm(inset)
|
|
q-separator.q-my-sm(inset)
|
|
q-item(tag='label', v-ripple)
|
|
q-item(tag='label', v-ripple)
|
|
blueprint-icon(icon='curly-arrow')
|
|
blueprint-icon(icon='curly-arrow')
|
|
q-item-section
|
|
q-item-section
|
|
- q-item-label {{$t(`admin.security.disallowOpenRedirect`)}}
|
|
|
|
- q-item-label(caption) {{$t(`admin.security.disallowOpenRedirectHint`)}}
|
|
|
|
|
|
+ q-item-label {{t(`admin.security.disallowOpenRedirect`)}}
|
|
|
|
+ q-item-label(caption) {{t(`admin.security.disallowOpenRedirectHint`)}}
|
|
q-item-section(avatar)
|
|
q-item-section(avatar)
|
|
q-toggle(
|
|
q-toggle(
|
|
- v-model='config.disallowOpenRedirect'
|
|
|
|
|
|
+ v-model='state.config.disallowOpenRedirect'
|
|
color='primary'
|
|
color='primary'
|
|
checked-icon='las la-check'
|
|
checked-icon='las la-check'
|
|
unchecked-icon='las la-times'
|
|
unchecked-icon='las la-times'
|
|
- :aria-label='$t(`admin.security.disallowOpenRedirect`)'
|
|
|
|
|
|
+ :aria-label='t(`admin.security.disallowOpenRedirect`)'
|
|
)
|
|
)
|
|
q-separator.q-my-sm(inset)
|
|
q-separator.q-my-sm(inset)
|
|
q-item(tag='label', v-ripple)
|
|
q-item(tag='label', v-ripple)
|
|
blueprint-icon(icon='download-from-cloud')
|
|
blueprint-icon(icon='download-from-cloud')
|
|
q-item-section
|
|
q-item-section
|
|
- q-item-label {{$t(`admin.security.forceAssetDownload`)}}
|
|
|
|
- q-item-label(caption) {{$t(`admin.security.forceAssetDownloadHint`)}}
|
|
|
|
|
|
+ q-item-label {{t(`admin.security.forceAssetDownload`)}}
|
|
|
|
+ q-item-label(caption) {{t(`admin.security.forceAssetDownloadHint`)}}
|
|
q-item-section(avatar)
|
|
q-item-section(avatar)
|
|
q-toggle(
|
|
q-toggle(
|
|
- v-model='config.forceAssetDownload'
|
|
|
|
|
|
+ v-model='state.config.forceAssetDownload'
|
|
color='primary'
|
|
color='primary'
|
|
checked-icon='las la-check'
|
|
checked-icon='las la-check'
|
|
unchecked-icon='las la-times'
|
|
unchecked-icon='las la-times'
|
|
- :aria-label='$t(`admin.security.forceAssetDownload`)'
|
|
|
|
|
|
+ :aria-label='t(`admin.security.forceAssetDownload`)'
|
|
)
|
|
)
|
|
q-separator.q-my-sm(inset)
|
|
q-separator.q-my-sm(inset)
|
|
q-item(tag='label', v-ripple)
|
|
q-item(tag='label', v-ripple)
|
|
blueprint-icon(icon='door-sensor-alarmed')
|
|
blueprint-icon(icon='door-sensor-alarmed')
|
|
q-item-section
|
|
q-item-section
|
|
- q-item-label {{$t(`admin.security.trustProxy`)}}
|
|
|
|
- q-item-label(caption) {{$t(`admin.security.trustProxyHint`)}}
|
|
|
|
|
|
+ q-item-label {{t(`admin.security.trustProxy`)}}
|
|
|
|
+ q-item-label(caption) {{t(`admin.security.trustProxyHint`)}}
|
|
q-item-section(avatar)
|
|
q-item-section(avatar)
|
|
q-toggle(
|
|
q-toggle(
|
|
- v-model='config.trustProxy'
|
|
|
|
|
|
+ v-model='state.config.trustProxy'
|
|
color='primary'
|
|
color='primary'
|
|
checked-icon='las la-check'
|
|
checked-icon='las la-check'
|
|
unchecked-icon='las la-times'
|
|
unchecked-icon='las la-times'
|
|
- :aria-label='$t(`admin.security.trustProxy`)'
|
|
|
|
|
|
+ :aria-label='t(`admin.security.trustProxy`)'
|
|
)
|
|
)
|
|
//- -----------------------
|
|
//- -----------------------
|
|
//- HSTS
|
|
//- HSTS
|
|
//- -----------------------
|
|
//- -----------------------
|
|
q-card.shadow-1.q-pb-sm.q-mt-md
|
|
q-card.shadow-1.q-pb-sm.q-mt-md
|
|
q-card-section
|
|
q-card-section
|
|
- .text-subtitle1 {{$t('admin.security.hsts')}}
|
|
|
|
|
|
+ .text-subtitle1 {{t('admin.security.hsts')}}
|
|
q-item(tag='label', v-ripple)
|
|
q-item(tag='label', v-ripple)
|
|
blueprint-icon(icon='hips')
|
|
blueprint-icon(icon='hips')
|
|
q-item-section
|
|
q-item-section
|
|
- q-item-label {{$t(`admin.security.enforceHsts`)}}
|
|
|
|
- q-item-label(caption) {{$t(`admin.security.enforceHstsHint`)}}
|
|
|
|
|
|
+ q-item-label {{t(`admin.security.enforceHsts`)}}
|
|
|
|
+ q-item-label(caption) {{t(`admin.security.enforceHstsHint`)}}
|
|
q-item-section(avatar)
|
|
q-item-section(avatar)
|
|
q-toggle(
|
|
q-toggle(
|
|
- v-model='config.enforceHsts'
|
|
|
|
|
|
+ v-model='state.config.enforceHsts'
|
|
color='primary'
|
|
color='primary'
|
|
checked-icon='las la-check'
|
|
checked-icon='las la-check'
|
|
unchecked-icon='las la-times'
|
|
unchecked-icon='las la-times'
|
|
- :aria-label='$t(`admin.security.enforceHsts`)'
|
|
|
|
|
|
+ :aria-label='t(`admin.security.enforceHsts`)'
|
|
)
|
|
)
|
|
- template(v-if='config.enforceHsts')
|
|
|
|
|
|
+ template(v-if='state.config.enforceHsts')
|
|
q-separator.q-my-sm(inset)
|
|
q-separator.q-my-sm(inset)
|
|
q-item
|
|
q-item
|
|
blueprint-icon(icon='timer')
|
|
blueprint-icon(icon='timer')
|
|
q-item-section
|
|
q-item-section
|
|
- q-item-label {{$t(`admin.security.hstsDuration`)}}
|
|
|
|
- q-item-label(caption) {{$t(`admin.security.hstsDurationHint`)}}
|
|
|
|
|
|
+ q-item-label {{t(`admin.security.hstsDuration`)}}
|
|
|
|
+ q-item-label(caption) {{t(`admin.security.hstsDurationHint`)}}
|
|
q-item-section(style='flex: 0 0 200px;')
|
|
q-item-section(style='flex: 0 0 200px;')
|
|
q-select(
|
|
q-select(
|
|
outlined
|
|
outlined
|
|
- v-model='config.hstsDuration'
|
|
|
|
|
|
+ v-model='state.config.hstsDuration'
|
|
:options='hstsDurations'
|
|
:options='hstsDurations'
|
|
option-value='value'
|
|
option-value='value'
|
|
option-label='text'
|
|
option-label='text'
|
|
emit-value
|
|
emit-value
|
|
map-options
|
|
map-options
|
|
dense
|
|
dense
|
|
- :aria-label='$t(`admin.security.hstsDuration`)'
|
|
|
|
|
|
+ :aria-label='t(`admin.security.hstsDuration`)'
|
|
)
|
|
)
|
|
|
|
|
|
.col-12.col-lg-6
|
|
.col-12.col-lg-6
|
|
@@ -174,53 +174,53 @@ q-page.admin-mail
|
|
//- -----------------------
|
|
//- -----------------------
|
|
q-card.shadow-1.q-pb-sm
|
|
q-card.shadow-1.q-pb-sm
|
|
q-card-section
|
|
q-card-section
|
|
- .text-subtitle1 {{$t('admin.security.uploads')}}
|
|
|
|
|
|
+ .text-subtitle1 {{t('admin.security.uploads')}}
|
|
q-item.q-pt-none
|
|
q-item.q-pt-none
|
|
q-item-section
|
|
q-item-section
|
|
q-card.bg-info.text-white.rounded-borders(flat)
|
|
q-card.bg-info.text-white.rounded-borders(flat)
|
|
q-card-section.items-center(horizontal)
|
|
q-card-section.items-center(horizontal)
|
|
q-card-section.col-auto.q-pr-none
|
|
q-card-section.col-auto.q-pr-none
|
|
q-icon(name='las la-info-circle', size='sm')
|
|
q-icon(name='las la-info-circle', size='sm')
|
|
- q-card-section.text-caption {{ $t('admin.security.uploadsInfo') }}
|
|
|
|
|
|
+ q-card-section.text-caption {{ t('admin.security.uploadsInfo') }}
|
|
q-item
|
|
q-item
|
|
blueprint-icon(icon='upload-to-the-cloud')
|
|
blueprint-icon(icon='upload-to-the-cloud')
|
|
q-item-section
|
|
q-item-section
|
|
- q-item-label {{$t(`admin.security.maxUploadSize`)}}
|
|
|
|
- q-item-label(caption) {{$t(`admin.security.maxUploadSizeHint`)}}
|
|
|
|
|
|
+ q-item-label {{t(`admin.security.maxUploadSize`)}}
|
|
|
|
+ q-item-label(caption) {{t(`admin.security.maxUploadSizeHint`)}}
|
|
q-item-section(style='flex: 0 0 200px;')
|
|
q-item-section(style='flex: 0 0 200px;')
|
|
q-input(
|
|
q-input(
|
|
outlined
|
|
outlined
|
|
- v-model.number='humanUploadMaxFileSize'
|
|
|
|
|
|
+ v-model.number='state.humanUploadMaxFileSize'
|
|
dense
|
|
dense
|
|
- :aria-label='$t(`admin.security.maxUploadSize`)'
|
|
|
|
|
|
+ :aria-label='t(`admin.security.maxUploadSize`)'
|
|
)
|
|
)
|
|
q-separator.q-my-sm(inset)
|
|
q-separator.q-my-sm(inset)
|
|
q-item
|
|
q-item
|
|
blueprint-icon(icon='upload-to-ftp')
|
|
blueprint-icon(icon='upload-to-ftp')
|
|
q-item-section
|
|
q-item-section
|
|
- q-item-label {{$t(`admin.security.maxUploadBatch`)}}
|
|
|
|
- q-item-label(caption) {{$t(`admin.security.maxUploadBatchHint`)}}
|
|
|
|
|
|
+ q-item-label {{t(`admin.security.maxUploadBatch`)}}
|
|
|
|
+ q-item-label(caption) {{t(`admin.security.maxUploadBatchHint`)}}
|
|
q-item-section(style='flex: 0 0 200px;')
|
|
q-item-section(style='flex: 0 0 200px;')
|
|
q-input(
|
|
q-input(
|
|
outlined
|
|
outlined
|
|
- v-model.number='config.uploadMaxFiles'
|
|
|
|
|
|
+ v-model.number='state.config.uploadMaxFiles'
|
|
dense
|
|
dense
|
|
- :suffix='$t(`admin.security.maxUploadBatchSuffix`)'
|
|
|
|
- :aria-label='$t(`admin.security.maxUploadBatch`)'
|
|
|
|
|
|
+ :suffix='t(`admin.security.maxUploadBatchSuffix`)'
|
|
|
|
+ :aria-label='t(`admin.security.maxUploadBatch`)'
|
|
)
|
|
)
|
|
q-separator.q-my-sm(inset)
|
|
q-separator.q-my-sm(inset)
|
|
q-item(tag='label', v-ripple)
|
|
q-item(tag='label', v-ripple)
|
|
blueprint-icon(icon='scan-stock')
|
|
blueprint-icon(icon='scan-stock')
|
|
q-item-section
|
|
q-item-section
|
|
- q-item-label {{$t(`admin.security.scanSVG`)}}
|
|
|
|
- q-item-label(caption) {{$t(`admin.security.scanSVGHint`)}}
|
|
|
|
|
|
+ q-item-label {{t(`admin.security.scanSVG`)}}
|
|
|
|
+ q-item-label(caption) {{t(`admin.security.scanSVGHint`)}}
|
|
q-item-section(avatar)
|
|
q-item-section(avatar)
|
|
q-toggle(
|
|
q-toggle(
|
|
- v-model='config.uploadScanSVG'
|
|
|
|
|
|
+ v-model='state.config.uploadScanSVG'
|
|
color='primary'
|
|
color='primary'
|
|
checked-icon='las la-check'
|
|
checked-icon='las la-check'
|
|
unchecked-icon='las la-times'
|
|
unchecked-icon='las la-times'
|
|
- :aria-label='$t(`admin.security.scanSVG`)'
|
|
|
|
|
|
+ :aria-label='t(`admin.security.scanSVG`)'
|
|
)
|
|
)
|
|
|
|
|
|
//- -----------------------
|
|
//- -----------------------
|
|
@@ -228,52 +228,52 @@ q-page.admin-mail
|
|
//- -----------------------
|
|
//- -----------------------
|
|
q-card.shadow-1.q-pb-sm.q-mt-md
|
|
q-card.shadow-1.q-pb-sm.q-mt-md
|
|
q-card-section
|
|
q-card-section
|
|
- .text-subtitle1 {{$t('admin.security.cors')}}
|
|
|
|
|
|
+ .text-subtitle1 {{t('admin.security.cors')}}
|
|
q-item
|
|
q-item
|
|
blueprint-icon(icon='firewall')
|
|
blueprint-icon(icon='firewall')
|
|
q-item-section
|
|
q-item-section
|
|
- q-item-label {{$t(`admin.security.corsMode`)}}
|
|
|
|
- q-item-label(caption) {{$t(`admin.security.corsModeHint`)}}
|
|
|
|
|
|
+ q-item-label {{t(`admin.security.corsMode`)}}
|
|
|
|
+ q-item-label(caption) {{t(`admin.security.corsModeHint`)}}
|
|
q-item-section
|
|
q-item-section
|
|
q-select(
|
|
q-select(
|
|
outlined
|
|
outlined
|
|
- v-model='config.corsMode'
|
|
|
|
|
|
+ v-model='state.config.corsMode'
|
|
:options='corsModes'
|
|
:options='corsModes'
|
|
option-value='value'
|
|
option-value='value'
|
|
option-label='text'
|
|
option-label='text'
|
|
emit-value
|
|
emit-value
|
|
map-options
|
|
map-options
|
|
dense
|
|
dense
|
|
- :aria-label='$t(`admin.security.corsMode`)'
|
|
|
|
|
|
+ :aria-label='t(`admin.security.corsMode`)'
|
|
)
|
|
)
|
|
- template(v-if='config.corsMode === `HOSTNAMES`')
|
|
|
|
|
|
+ template(v-if='state.config.corsMode === `HOSTNAMES`')
|
|
q-separator.q-my-sm(inset)
|
|
q-separator.q-my-sm(inset)
|
|
q-item
|
|
q-item
|
|
blueprint-icon(icon='todo-list', key='corsHostnames')
|
|
blueprint-icon(icon='todo-list', key='corsHostnames')
|
|
q-item-section
|
|
q-item-section
|
|
- q-item-label {{$t(`admin.security.corsHostnames`)}}
|
|
|
|
- q-item-label(caption) {{$t(`admin.security.corsHostnamesHint`)}}
|
|
|
|
|
|
+ q-item-label {{t(`admin.security.corsHostnames`)}}
|
|
|
|
+ q-item-label(caption) {{t(`admin.security.corsHostnamesHint`)}}
|
|
q-item-section
|
|
q-item-section
|
|
q-input(
|
|
q-input(
|
|
outlined
|
|
outlined
|
|
- v-model='config.corsConfig'
|
|
|
|
|
|
+ v-model='state.config.corsConfig'
|
|
dense
|
|
dense
|
|
type='textarea'
|
|
type='textarea'
|
|
- :aria-label='$t(`admin.security.corsHostnames`)'
|
|
|
|
|
|
+ :aria-label='t(`admin.security.corsHostnames`)'
|
|
)
|
|
)
|
|
- template(v-else-if='config.corsMode === `REGEX`')
|
|
|
|
|
|
+ template(v-else-if='state.config.corsMode === `REGEX`')
|
|
q-separator.q-my-sm(inset)
|
|
q-separator.q-my-sm(inset)
|
|
q-item
|
|
q-item
|
|
blueprint-icon(icon='validation', key='corsRegex')
|
|
blueprint-icon(icon='validation', key='corsRegex')
|
|
q-item-section
|
|
q-item-section
|
|
- q-item-label {{$t(`admin.security.corsRegex`)}}
|
|
|
|
- q-item-label(caption) {{$t(`admin.security.corsRegexHint`)}}
|
|
|
|
|
|
+ q-item-label {{t(`admin.security.corsRegex`)}}
|
|
|
|
+ q-item-label(caption) {{t(`admin.security.corsRegexHint`)}}
|
|
q-item-section
|
|
q-item-section
|
|
q-input(
|
|
q-input(
|
|
outlined
|
|
outlined
|
|
- v-model='config.corsConfig'
|
|
|
|
|
|
+ v-model='state.config.corsConfig'
|
|
dense
|
|
dense
|
|
- :aria-label='$t(`admin.security.corsRegex`)'
|
|
|
|
|
|
+ :aria-label='t(`admin.security.corsRegex`)'
|
|
)
|
|
)
|
|
|
|
|
|
//- -----------------------
|
|
//- -----------------------
|
|
@@ -281,220 +281,241 @@ q-page.admin-mail
|
|
//- -----------------------
|
|
//- -----------------------
|
|
q-card.shadow-1.q-pb-sm.q-mt-md
|
|
q-card.shadow-1.q-pb-sm.q-mt-md
|
|
q-card-section
|
|
q-card-section
|
|
- .text-subtitle1 {{$t('admin.security.jwt')}}
|
|
|
|
|
|
+ .text-subtitle1 {{t('admin.security.jwt')}}
|
|
q-item
|
|
q-item
|
|
blueprint-icon(icon='ticket')
|
|
blueprint-icon(icon='ticket')
|
|
q-item-section
|
|
q-item-section
|
|
- q-item-label {{$t(`admin.security.jwtAudience`)}}
|
|
|
|
- q-item-label(caption) {{$t(`admin.security.jwtAudienceHint`)}}
|
|
|
|
|
|
+ q-item-label {{t(`admin.security.jwtAudience`)}}
|
|
|
|
+ q-item-label(caption) {{t(`admin.security.jwtAudienceHint`)}}
|
|
q-item-section(style='flex: 0 0 250px;')
|
|
q-item-section(style='flex: 0 0 250px;')
|
|
q-input(
|
|
q-input(
|
|
outlined
|
|
outlined
|
|
- v-model='config.authJwtAudience'
|
|
|
|
|
|
+ v-model='state.config.authJwtAudience'
|
|
dense
|
|
dense
|
|
- :aria-label='$t(`admin.security.jwtAudience`)'
|
|
|
|
|
|
+ :aria-label='t(`admin.security.jwtAudience`)'
|
|
)
|
|
)
|
|
q-separator.q-my-sm(inset)
|
|
q-separator.q-my-sm(inset)
|
|
q-item
|
|
q-item
|
|
blueprint-icon(icon='expired')
|
|
blueprint-icon(icon='expired')
|
|
q-item-section
|
|
q-item-section
|
|
- q-item-label {{$t(`admin.security.tokenExpiration`)}}
|
|
|
|
- q-item-label(caption) {{$t(`admin.security.tokenExpirationHint`)}}
|
|
|
|
|
|
+ q-item-label {{t(`admin.security.tokenExpiration`)}}
|
|
|
|
+ q-item-label(caption) {{t(`admin.security.tokenExpirationHint`)}}
|
|
q-item-section(style='flex: 0 0 140px;')
|
|
q-item-section(style='flex: 0 0 140px;')
|
|
q-input(
|
|
q-input(
|
|
outlined
|
|
outlined
|
|
- v-model='config.authJwtExpiration'
|
|
|
|
|
|
+ v-model='state.config.authJwtExpiration'
|
|
dense
|
|
dense
|
|
- :aria-label='$t(`admin.security.tokenExpiration`)'
|
|
|
|
|
|
+ :aria-label='t(`admin.security.tokenExpiration`)'
|
|
)
|
|
)
|
|
q-separator.q-my-sm(inset)
|
|
q-separator.q-my-sm(inset)
|
|
q-item
|
|
q-item
|
|
blueprint-icon(icon='future')
|
|
blueprint-icon(icon='future')
|
|
q-item-section
|
|
q-item-section
|
|
- q-item-label {{$t(`admin.security.tokenRenewalPeriod`)}}
|
|
|
|
- q-item-label(caption) {{$t(`admin.security.tokenRenewalPeriodHint`)}}
|
|
|
|
|
|
+ q-item-label {{t(`admin.security.tokenRenewalPeriod`)}}
|
|
|
|
+ q-item-label(caption) {{t(`admin.security.tokenRenewalPeriodHint`)}}
|
|
q-item-section(style='flex: 0 0 140px;')
|
|
q-item-section(style='flex: 0 0 140px;')
|
|
q-input(
|
|
q-input(
|
|
outlined
|
|
outlined
|
|
- v-model='config.authJwtRenewablePeriod'
|
|
|
|
|
|
+ v-model='state.config.authJwtRenewablePeriod'
|
|
dense
|
|
dense
|
|
- :aria-label='$t(`admin.security.tokenRenewalPeriod`)'
|
|
|
|
|
|
+ :aria-label='t(`admin.security.tokenRenewalPeriod`)'
|
|
)
|
|
)
|
|
</template>
|
|
</template>
|
|
|
|
|
|
-<script>
|
|
|
|
|
|
+<script setup>
|
|
import cloneDeep from 'lodash/cloneDeep'
|
|
import cloneDeep from 'lodash/cloneDeep'
|
|
import gql from 'graphql-tag'
|
|
import gql from 'graphql-tag'
|
|
import _get from 'lodash/get'
|
|
import _get from 'lodash/get'
|
|
import filesize from 'filesize'
|
|
import filesize from 'filesize'
|
|
import filesizeParser from 'filesize-parser'
|
|
import filesizeParser from 'filesize-parser'
|
|
-import { createMetaMixin } from 'quasar'
|
|
|
|
|
|
|
|
-export default {
|
|
|
|
- mixins: [
|
|
|
|
- createMetaMixin(function () {
|
|
|
|
- return {
|
|
|
|
- title: this.$t('admin.security.title')
|
|
|
|
- }
|
|
|
|
- })
|
|
|
|
- ],
|
|
|
|
- data () {
|
|
|
|
- return {
|
|
|
|
- loading: false,
|
|
|
|
- config: {
|
|
|
|
- corsConfig: '',
|
|
|
|
- corsMode: 'OFF',
|
|
|
|
- cspDirectives: '',
|
|
|
|
- disallowFloc: false,
|
|
|
|
- disallowIframe: false,
|
|
|
|
- disallowOpenRedirect: false,
|
|
|
|
- enforceCsp: false,
|
|
|
|
- enforceHsts: false,
|
|
|
|
- enforceSameOriginReferrerPolicy: false,
|
|
|
|
- forceAssetDownload: false,
|
|
|
|
- hstsDuration: 0,
|
|
|
|
- trustProxy: false,
|
|
|
|
- authJwtAudience: 'urn:wiki.js',
|
|
|
|
- authJwtExpiration: '30m',
|
|
|
|
- authJwtRenewablePeriod: '14d',
|
|
|
|
- uploadMaxFileSize: 0,
|
|
|
|
- uploadMaxFiles: 0,
|
|
|
|
- uploadScanSVG: false
|
|
|
|
- },
|
|
|
|
- humanUploadMaxFileSize: '0',
|
|
|
|
- hstsDurations: [
|
|
|
|
- { value: 300, text: '5 minutes' },
|
|
|
|
- { value: 86400, text: '1 day' },
|
|
|
|
- { value: 604800, text: '1 week' },
|
|
|
|
- { value: 2592000, text: '1 month' },
|
|
|
|
- { value: 31536000, text: '1 year' },
|
|
|
|
- { value: 63072000, text: '2 years' }
|
|
|
|
- ]
|
|
|
|
- }
|
|
|
|
- },
|
|
|
|
- computed: {
|
|
|
|
- corsModes () {
|
|
|
|
- return [
|
|
|
|
- { value: 'OFF', text: 'Off / Same-Origin' },
|
|
|
|
- { value: 'REFLECT', text: 'Reflect Request Origin' },
|
|
|
|
- { value: 'HOSTNAMES', text: 'Hostnames Whitelist' },
|
|
|
|
- { value: 'REGEX', text: 'Regex Pattern Match' }
|
|
|
|
- ]
|
|
|
|
- }
|
|
|
|
- },
|
|
|
|
- mounted () {
|
|
|
|
- this.load()
|
|
|
|
|
|
+import { useI18n } from 'vue-i18n'
|
|
|
|
+import { useMeta, useQuasar } from 'quasar'
|
|
|
|
+import { computed, onMounted, reactive, watch } from 'vue'
|
|
|
|
+
|
|
|
|
+import { useAdminStore } from 'src/stores/admin'
|
|
|
|
+import { useSiteStore } from 'src/stores/site'
|
|
|
|
+import { useDataStore } from 'src/stores/data'
|
|
|
|
+
|
|
|
|
+// QUASAR
|
|
|
|
+
|
|
|
|
+const $q = useQuasar()
|
|
|
|
+
|
|
|
|
+// STORES
|
|
|
|
+
|
|
|
|
+const adminStore = useAdminStore()
|
|
|
|
+const siteStore = useSiteStore()
|
|
|
|
+const dataStore = useDataStore()
|
|
|
|
+
|
|
|
|
+// I18N
|
|
|
|
+
|
|
|
|
+const { t } = useI18n()
|
|
|
|
+
|
|
|
|
+// META
|
|
|
|
+
|
|
|
|
+useMeta({
|
|
|
|
+ title: t('admin.security.title')
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+// DATA
|
|
|
|
+
|
|
|
|
+const state = reactive({
|
|
|
|
+ loading: false,
|
|
|
|
+ config: {
|
|
|
|
+ corsConfig: '',
|
|
|
|
+ corsMode: 'OFF',
|
|
|
|
+ cspDirectives: '',
|
|
|
|
+ disallowFloc: false,
|
|
|
|
+ disallowIframe: false,
|
|
|
|
+ disallowOpenRedirect: false,
|
|
|
|
+ enforceCsp: false,
|
|
|
|
+ enforceHsts: false,
|
|
|
|
+ enforceSameOriginReferrerPolicy: false,
|
|
|
|
+ forceAssetDownload: false,
|
|
|
|
+ hstsDuration: 0,
|
|
|
|
+ trustProxy: false,
|
|
|
|
+ authJwtAudience: 'urn:wiki.js',
|
|
|
|
+ authJwtExpiration: '30m',
|
|
|
|
+ authJwtRenewablePeriod: '14d',
|
|
|
|
+ uploadMaxFileSize: 0,
|
|
|
|
+ uploadMaxFiles: 0,
|
|
|
|
+ uploadScanSVG: false
|
|
},
|
|
},
|
|
- methods: {
|
|
|
|
- async load () {
|
|
|
|
- this.loading++
|
|
|
|
- this.$q.loading.show()
|
|
|
|
- const resp = await this.$apollo.query({
|
|
|
|
- query: gql`
|
|
|
|
- query getSecurityConfig {
|
|
|
|
- systemSecurity {
|
|
|
|
- authJwtAudience
|
|
|
|
- authJwtExpiration
|
|
|
|
- authJwtRenewablePeriod
|
|
|
|
- corsConfig
|
|
|
|
- corsMode
|
|
|
|
- cspDirectives
|
|
|
|
- disallowFloc
|
|
|
|
- disallowIframe
|
|
|
|
- disallowOpenRedirect
|
|
|
|
- enforceCsp
|
|
|
|
- enforceHsts
|
|
|
|
- enforceSameOriginReferrerPolicy
|
|
|
|
- forceAssetDownload
|
|
|
|
- hstsDuration
|
|
|
|
- trustProxy
|
|
|
|
- uploadMaxFileSize
|
|
|
|
- uploadMaxFiles
|
|
|
|
- uploadScanSVG
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- `,
|
|
|
|
- fetchPolicy: 'network-only'
|
|
|
|
- })
|
|
|
|
- this.config = cloneDeep(resp?.data?.systemSecurity)
|
|
|
|
- this.humanUploadMaxFileSize = filesize(this.config.uploadMaxFileSize ?? 0, { base: 2, standard: 'jedec' })
|
|
|
|
- this.$q.loading.hide()
|
|
|
|
- this.loading--
|
|
|
|
- },
|
|
|
|
- async save () {
|
|
|
|
- this.loading = true
|
|
|
|
- try {
|
|
|
|
- const respRaw = await this.$apollo.mutate({
|
|
|
|
- mutation: gql`
|
|
|
|
- mutation saveSecurityConfig (
|
|
|
|
- $authJwtAudience: String
|
|
|
|
- $authJwtExpiration: String
|
|
|
|
- $authJwtRenewablePeriod: String
|
|
|
|
- $corsConfig: String
|
|
|
|
- $corsMode: SystemSecurityCorsMode
|
|
|
|
- $cspDirectives: String
|
|
|
|
- $disallowFloc: Boolean
|
|
|
|
- $disallowIframe: Boolean
|
|
|
|
- $disallowOpenRedirect: Boolean
|
|
|
|
- $enforceCsp: Boolean
|
|
|
|
- $enforceHsts: Boolean
|
|
|
|
- $enforceSameOriginReferrerPolicy: Boolean
|
|
|
|
- $hstsDuration: Int
|
|
|
|
- $trustProxy: Boolean
|
|
|
|
- $uploadMaxFiles: Int
|
|
|
|
- $uploadMaxFileSize: Int
|
|
|
|
- ) {
|
|
|
|
- updateSystemSecurity(
|
|
|
|
- authJwtAudience: $authJwtAudience
|
|
|
|
- authJwtExpiration: $authJwtExpiration
|
|
|
|
- authJwtRenewablePeriod: $authJwtRenewablePeriod
|
|
|
|
- corsConfig: $corsConfig
|
|
|
|
- corsMode: $corsMode
|
|
|
|
- cspDirectives: $cspDirectives
|
|
|
|
- disallowFloc: $disallowFloc
|
|
|
|
- disallowIframe: $disallowIframe
|
|
|
|
- disallowOpenRedirect: $disallowOpenRedirect
|
|
|
|
- enforceCsp: $enforceCsp
|
|
|
|
- enforceHsts: $enforceHsts
|
|
|
|
- enforceSameOriginReferrerPolicy: $enforceSameOriginReferrerPolicy
|
|
|
|
- hstsDuration: $hstsDuration
|
|
|
|
- trustProxy: $trustProxy
|
|
|
|
- uploadMaxFiles: $uploadMaxFiles
|
|
|
|
- uploadMaxFileSize: $uploadMaxFileSize
|
|
|
|
- ) {
|
|
|
|
- status {
|
|
|
|
- succeeded
|
|
|
|
- slug
|
|
|
|
- message
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ humanUploadMaxFileSize: '0'
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+const hstsDurations = [
|
|
|
|
+ { value: 300, text: '5 minutes' },
|
|
|
|
+ { value: 86400, text: '1 day' },
|
|
|
|
+ { value: 604800, text: '1 week' },
|
|
|
|
+ { value: 2592000, text: '1 month' },
|
|
|
|
+ { value: 31536000, text: '1 year' },
|
|
|
|
+ { value: 63072000, text: '2 years' }
|
|
|
|
+]
|
|
|
|
+
|
|
|
|
+const corsModes = [
|
|
|
|
+ { value: 'OFF', text: 'Off / Same-Origin' },
|
|
|
|
+ { value: 'REFLECT', text: 'Reflect Request Origin' },
|
|
|
|
+ { value: 'HOSTNAMES', text: 'Hostnames Whitelist' },
|
|
|
|
+ { value: 'REGEX', text: 'Regex Pattern Match' }
|
|
|
|
+]
|
|
|
|
+
|
|
|
|
+// METHODS
|
|
|
|
+
|
|
|
|
+async function load () {
|
|
|
|
+ state.loading++
|
|
|
|
+ $q.loading.show()
|
|
|
|
+ const resp = await APOLLO_CLIENT.query({
|
|
|
|
+ query: gql`
|
|
|
|
+ query getSecurityConfig {
|
|
|
|
+ systemSecurity {
|
|
|
|
+ authJwtAudience
|
|
|
|
+ authJwtExpiration
|
|
|
|
+ authJwtRenewablePeriod
|
|
|
|
+ corsConfig
|
|
|
|
+ corsMode
|
|
|
|
+ cspDirectives
|
|
|
|
+ disallowFloc
|
|
|
|
+ disallowIframe
|
|
|
|
+ disallowOpenRedirect
|
|
|
|
+ enforceCsp
|
|
|
|
+ enforceHsts
|
|
|
|
+ enforceSameOriginReferrerPolicy
|
|
|
|
+ forceAssetDownload
|
|
|
|
+ hstsDuration
|
|
|
|
+ trustProxy
|
|
|
|
+ uploadMaxFileSize
|
|
|
|
+ uploadMaxFiles
|
|
|
|
+ uploadScanSVG
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ `,
|
|
|
|
+ fetchPolicy: 'network-only'
|
|
|
|
+ })
|
|
|
|
+ state.config = cloneDeep(resp?.data?.systemSecurity)
|
|
|
|
+ state.humanUploadMaxFileSize = filesize(state.config.uploadMaxFileSize ?? 0, { base: 2, standard: 'jedec' })
|
|
|
|
+ $q.loading.hide()
|
|
|
|
+ state.loading--
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+async function save () {
|
|
|
|
+ state.loading++
|
|
|
|
+ try {
|
|
|
|
+ const respRaw = await APOLLO_CLIENT.mutate({
|
|
|
|
+ mutation: gql`
|
|
|
|
+ mutation saveSecurityConfig (
|
|
|
|
+ $authJwtAudience: String
|
|
|
|
+ $authJwtExpiration: String
|
|
|
|
+ $authJwtRenewablePeriod: String
|
|
|
|
+ $corsConfig: String
|
|
|
|
+ $corsMode: SystemSecurityCorsMode
|
|
|
|
+ $cspDirectives: String
|
|
|
|
+ $disallowFloc: Boolean
|
|
|
|
+ $disallowIframe: Boolean
|
|
|
|
+ $disallowOpenRedirect: Boolean
|
|
|
|
+ $enforceCsp: Boolean
|
|
|
|
+ $enforceHsts: Boolean
|
|
|
|
+ $enforceSameOriginReferrerPolicy: Boolean
|
|
|
|
+ $hstsDuration: Int
|
|
|
|
+ $trustProxy: Boolean
|
|
|
|
+ $uploadMaxFiles: Int
|
|
|
|
+ $uploadMaxFileSize: Int
|
|
|
|
+ ) {
|
|
|
|
+ updateSystemSecurity(
|
|
|
|
+ authJwtAudience: $authJwtAudience
|
|
|
|
+ authJwtExpiration: $authJwtExpiration
|
|
|
|
+ authJwtRenewablePeriod: $authJwtRenewablePeriod
|
|
|
|
+ corsConfig: $corsConfig
|
|
|
|
+ corsMode: $corsMode
|
|
|
|
+ cspDirectives: $cspDirectives
|
|
|
|
+ disallowFloc: $disallowFloc
|
|
|
|
+ disallowIframe: $disallowIframe
|
|
|
|
+ disallowOpenRedirect: $disallowOpenRedirect
|
|
|
|
+ enforceCsp: $enforceCsp
|
|
|
|
+ enforceHsts: $enforceHsts
|
|
|
|
+ enforceSameOriginReferrerPolicy: $enforceSameOriginReferrerPolicy
|
|
|
|
+ hstsDuration: $hstsDuration
|
|
|
|
+ trustProxy: $trustProxy
|
|
|
|
+ uploadMaxFiles: $uploadMaxFiles
|
|
|
|
+ uploadMaxFileSize: $uploadMaxFileSize
|
|
|
|
+ ) {
|
|
|
|
+ status {
|
|
|
|
+ succeeded
|
|
|
|
+ slug
|
|
|
|
+ message
|
|
}
|
|
}
|
|
- `,
|
|
|
|
- variables: {
|
|
|
|
- ...this.config,
|
|
|
|
- uploadMaxFileSize: filesizeParser(this.humanUploadMaxFileSize || '0')
|
|
|
|
}
|
|
}
|
|
- })
|
|
|
|
- const resp = _get(respRaw, 'data.updateSystemSecurity.status', {})
|
|
|
|
- if (resp.succeeded) {
|
|
|
|
- this.$q.notify({
|
|
|
|
- type: 'positive',
|
|
|
|
- message: this.$t('admin.security.saveSuccess')
|
|
|
|
- })
|
|
|
|
- } else {
|
|
|
|
- throw new Error(resp.message)
|
|
|
|
}
|
|
}
|
|
- } catch (err) {
|
|
|
|
- this.$q.notify({
|
|
|
|
- type: 'negative',
|
|
|
|
- message: 'Failed to save security config',
|
|
|
|
- caption: err.message
|
|
|
|
- })
|
|
|
|
|
|
+ `,
|
|
|
|
+ variables: {
|
|
|
|
+ ...state.config,
|
|
|
|
+ uploadMaxFileSize: filesizeParser(state.humanUploadMaxFileSize || '0')
|
|
}
|
|
}
|
|
- this.loading = false
|
|
|
|
|
|
+ })
|
|
|
|
+ const resp = _get(respRaw, 'data.updateSystemSecurity.status', {})
|
|
|
|
+ if (resp.succeeded) {
|
|
|
|
+ $q.notify({
|
|
|
|
+ type: 'positive',
|
|
|
|
+ message: t('admin.security.saveSuccess')
|
|
|
|
+ })
|
|
|
|
+ } else {
|
|
|
|
+ throw new Error(resp.message)
|
|
}
|
|
}
|
|
|
|
+ } catch (err) {
|
|
|
|
+ $q.notify({
|
|
|
|
+ type: 'negative',
|
|
|
|
+ message: 'Failed to save security config',
|
|
|
|
+ caption: err.message
|
|
|
|
+ })
|
|
}
|
|
}
|
|
|
|
+ state.loading--
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+// MOUNTED
|
|
|
|
+
|
|
|
|
+onMounted(() => {
|
|
|
|
+ load()
|
|
|
|
+})
|
|
</script>
|
|
</script>
|
|
|
|
|
|
<style lang='scss'>
|
|
<style lang='scss'>
|