admin-security.vue 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. <template lang='pug'>
  2. v-container(fluid, grid-list-lg)
  3. v-layout(row wrap)
  4. v-flex(xs12)
  5. .admin-header
  6. img.animated.fadeInUp(src='/_assets/svg/icon-private.svg', alt='Security', style='width: 80px;')
  7. .admin-header-title
  8. .headline.primary--text.animated.fadeInLeft {{ $t('admin:security.title') }}
  9. .subtitle-1.grey--text.animated.fadeInLeft {{ $t('admin:security.subtitle') }}
  10. v-spacer
  11. v-btn.animated.fadeInDown(color='success', depressed, @click='save', large)
  12. v-icon(left) mdi-check
  13. span {{$t('common:actions.apply')}}
  14. v-form.pt-3
  15. v-layout(row wrap)
  16. v-flex(lg6 xs12)
  17. v-card.animated.fadeInUp
  18. v-toolbar(color='red darken-2', dark, dense, flat)
  19. v-toolbar-title.subtitle-1 Security
  20. v-card-info(color='red')
  21. span Make sure to understand the implications before turning on / off a security feature.
  22. v-card-text
  23. v-switch.mt-3(
  24. inset
  25. label='Block IFrame Embedding'
  26. color='red darken-2'
  27. v-model='config.securityIframe'
  28. persistent-hint
  29. hint='Prevents other websites from embedding your wiki in an iframe. This provides clickjacking protection.'
  30. )
  31. v-divider.mt-3
  32. v-switch(
  33. inset
  34. label='Same Origin Referrer Policy'
  35. color='red darken-2'
  36. v-model='config.securityReferrerPolicy'
  37. persistent-hint
  38. hint='Limits the referrer header to same origin.'
  39. )
  40. v-divider.mt-3
  41. v-switch(
  42. inset
  43. label='Trust X-Forwarded-* Proxy Headers'
  44. color='red darken-2'
  45. v-model='config.securityTrustProxy'
  46. persistent-hint
  47. hint='Should be enabled when using a reverse-proxy like nginx, apache, CloudFlare, etc in front of Wiki.js. Turn off otherwise.'
  48. )
  49. //- v-divider.mt-3
  50. //- v-switch(
  51. //- inset
  52. //- label='Subresource Integrity (SRI)'
  53. //- color='red darken-2'
  54. //- v-model='config.securitySRI'
  55. //- persistent-hint
  56. //- hint='This ensure that resources such as CSS and JS files are not altered during delivery.'
  57. //- disabled
  58. //- )
  59. v-divider.mt-3
  60. v-switch(
  61. inset
  62. label='Enforce HSTS'
  63. color='red darken-2'
  64. v-model='config.securityHSTS'
  65. persistent-hint
  66. hint='This ensures the connection cannot be established through an insecure HTTP connection.'
  67. )
  68. v-select.mt-5(
  69. outlined
  70. label='HSTS Max Age'
  71. :items='hstsDurations'
  72. v-model='config.securityHSTSDuration'
  73. prepend-icon='mdi-subdirectory-arrow-right'
  74. :disabled='!config.securityHSTS'
  75. hide-details
  76. style='max-width: 450px;'
  77. )
  78. .pl-11.mt-3
  79. .caption Defines the duration for which the server should only deliver content through HTTPS.
  80. .caption It's a good idea to start with small values and make sure that nothing breaks on your wiki before moving to longer values.
  81. v-divider.mt-3
  82. v-switch(
  83. inset
  84. label='Enforce CSP'
  85. color='red darken-2'
  86. v-model='config.securityCSP'
  87. persistent-hint
  88. hint='Restricts scripts to pre-approved content sources.'
  89. disabled
  90. )
  91. v-textarea.mt-5(
  92. label='CSP Directives'
  93. outlined
  94. v-model='config.securityCSPDirectives'
  95. prepend-icon='mdi-subdirectory-arrow-right'
  96. persistent-hint
  97. hint='One directive per line.'
  98. disabled
  99. )
  100. v-flex(lg6 xs12)
  101. v-card.animated.fadeInUp.wait-p2s
  102. v-toolbar(color='primary', dark, dense, flat)
  103. v-toolbar-title.subtitle-1 {{ $t('admin:security.uploads') }}
  104. v-card-info(color='blue')
  105. span {{$t('admin:security.uploadsInfo')}}
  106. v-card-text
  107. v-text-field.mt-3(
  108. outlined
  109. :label='$t(`admin:security.maxUploadSize`)'
  110. required
  111. v-model='config.uploadMaxFileSize'
  112. prepend-icon='mdi-progress-upload'
  113. :hint='$t(`admin:security.maxUploadSizeHint`)'
  114. persistent-hint
  115. :suffix='$t(`admin:security.maxUploadSizeSuffix`)'
  116. style='max-width: 450px;'
  117. )
  118. v-text-field.mt-3(
  119. outlined
  120. :label='$t(`admin:security.maxUploadBatch`)'
  121. required
  122. v-model='config.uploadMaxFiles'
  123. prepend-icon='mdi-upload-lock'
  124. :hint='$t(`admin:security.maxUploadBatchHint`)'
  125. persistent-hint
  126. :suffix='$t(`admin:security.maxUploadBatchSuffix`)'
  127. style='max-width: 450px;'
  128. )
  129. </template>
  130. <script>
  131. import _ from 'lodash'
  132. import { sync } from 'vuex-pathify'
  133. import gql from 'graphql-tag'
  134. export default {
  135. data() {
  136. return {
  137. config: {
  138. uploadMaxFileSize: 0,
  139. uploadMaxFiles: 0,
  140. securityIframe: true,
  141. securityReferrerPolicy: true,
  142. securityTrustProxy: true,
  143. securitySRI: true,
  144. securityHSTS: false,
  145. securityHSTSDuration: 0,
  146. securityCSP: false,
  147. securityCSPDirectives: ''
  148. },
  149. hstsDurations: [
  150. { value: 300, text: '5 minutes' },
  151. { value: 86400, text: '1 day' },
  152. { value: 604800, text: '1 week' },
  153. { value: 2592000, text: '1 month' },
  154. { value: 31536000, text: '1 year' },
  155. { value: 63072000, text: '2 years' }
  156. ]
  157. }
  158. },
  159. computed: {
  160. activeModal: sync('editor/activeModal')
  161. },
  162. methods: {
  163. async save () {
  164. try {
  165. await this.$apollo.mutate({
  166. mutation: gql`
  167. mutation (
  168. $uploadMaxFileSize: Int
  169. $uploadMaxFiles: Int
  170. $securityIframe: Boolean
  171. $securityReferrerPolicy: Boolean
  172. $securityTrustProxy: Boolean
  173. $securitySRI: Boolean
  174. $securityHSTS: Boolean
  175. $securityHSTSDuration: Int
  176. $securityCSP: Boolean
  177. $securityCSPDirectives: String
  178. ) {
  179. site {
  180. updateConfig(
  181. uploadMaxFileSize: $uploadMaxFileSize,
  182. uploadMaxFiles: $uploadMaxFiles,
  183. securityIframe: $securityIframe,
  184. securityReferrerPolicy: $securityReferrerPolicy,
  185. securityTrustProxy: $securityTrustProxy,
  186. securitySRI: $securitySRI,
  187. securityHSTS: $securityHSTS,
  188. securityHSTSDuration: $securityHSTSDuration,
  189. securityCSP: $securityCSP,
  190. securityCSPDirectives: $securityCSPDirectives
  191. ) {
  192. responseResult {
  193. succeeded
  194. errorCode
  195. slug
  196. message
  197. }
  198. }
  199. }
  200. }
  201. `,
  202. variables: {
  203. uploadMaxFileSize: _.toSafeInteger(_.get(this.config, 'uploadMaxFileSize', 0)),
  204. uploadMaxFiles: _.toSafeInteger(_.get(this.config, 'uploadMaxFiles', 0)),
  205. securityIframe: _.get(this.config, 'securityIframe', false),
  206. securityReferrerPolicy: _.get(this.config, 'securityReferrerPolicy', false),
  207. securityTrustProxy: _.get(this.config, 'securityTrustProxy', false),
  208. securitySRI: _.get(this.config, 'securitySRI', false),
  209. securityHSTS: _.get(this.config, 'securityHSTS', false),
  210. securityHSTSDuration: _.get(this.config, 'securityHSTSDuration', 0),
  211. securityCSP: _.get(this.config, 'securityCSP', false),
  212. securityCSPDirectives: _.get(this.config, 'securityCSPDirectives', '')
  213. },
  214. watchLoading (isLoading) {
  215. this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-site-update')
  216. }
  217. })
  218. this.$store.commit('showNotification', {
  219. style: 'success',
  220. message: 'Configuration saved successfully.',
  221. icon: 'check'
  222. })
  223. } catch (err) {
  224. this.$store.commit('pushGraphError', err)
  225. }
  226. }
  227. },
  228. apollo: {
  229. config: {
  230. query: gql`
  231. {
  232. site {
  233. config {
  234. uploadMaxFileSize
  235. uploadMaxFiles
  236. securityIframe
  237. securityReferrerPolicy
  238. securityTrustProxy
  239. securitySRI
  240. securityHSTS
  241. securityHSTSDuration
  242. securityCSP
  243. securityCSPDirectives
  244. }
  245. }
  246. }
  247. `,
  248. fetchPolicy: 'network-only',
  249. update: (data) => _.cloneDeep(data.site.config),
  250. watchLoading (isLoading) {
  251. this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-security-refresh')
  252. }
  253. }
  254. }
  255. }
  256. </script>
  257. <style lang='scss'>
  258. </style>