admin-security.vue 11 KB

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