You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

283 lines
11 KiB

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