AdminTwoFactor.vue 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. <template>
  2. <NcSettingsSection :name="t('settings', 'Two-Factor Authentication')"
  3. :description="t('settings', 'Two-factor authentication can be enforced for all users and specific groups. If they do not have a two-factor provider configured, they will be unable to log into the system.')"
  4. :doc-url="twoFactorAdminDoc">
  5. <p v-if="loading">
  6. <span class="icon-loading-small two-factor-loading" />
  7. <span>{{ t('settings', 'Enforce two-factor authentication') }}</span>
  8. </p>
  9. <NcCheckboxRadioSwitch v-else
  10. id="two-factor-enforced"
  11. :checked.sync="enforced"
  12. type="switch">
  13. {{ t('settings', 'Enforce two-factor authentication') }}
  14. </NcCheckboxRadioSwitch>
  15. <template v-if="enforced">
  16. <h3>{{ t('settings', 'Limit to groups') }}</h3>
  17. {{ t('settings', 'Enforcement of two-factor authentication can be set for certain groups only.') }}
  18. <p class="top-margin">
  19. {{ t('settings', 'Two-factor authentication is enforced for all members of the following groups.') }}
  20. </p>
  21. <p>
  22. <label for="enforcedGroups">
  23. <span>{{ t('settings', 'Enforced groups') }}</span>
  24. </label>
  25. <NcSelect v-model="enforcedGroups"
  26. input-id="enforcedGroups"
  27. :options="groups"
  28. :disabled="loading"
  29. :multiple="true"
  30. :loading="loadingGroups"
  31. :close-on-select="false"
  32. @search="searchGroup" />
  33. </p>
  34. <p class="top-margin">
  35. {{ t('settings', 'Two-factor authentication is not enforced for members of the following groups.') }}
  36. </p>
  37. <p>
  38. <label for="excludedGroups">
  39. <span>{{ t('settings', 'Excluded groups') }}</span>
  40. </label>
  41. <NcSelect v-model="excludedGroups"
  42. input-id="excludedGroups"
  43. :options="groups"
  44. :disabled="loading"
  45. :multiple="true"
  46. :loading="loadingGroups"
  47. :close-on-select="false"
  48. @search="searchGroup" />
  49. </p>
  50. <p class="top-margin">
  51. <em>
  52. <!-- this text is also found in the documentation. update it there as well if it ever changes -->
  53. {{ t('settings', 'When groups are selected/excluded, they use the following logic to determine if a user has 2FA enforced: If no groups are selected, 2FA is enabled for everyone except members of the excluded groups. If groups are selected, 2FA is enabled for all members of these. If a user is both in a selected and excluded group, the selected takes precedence and 2FA is enforced.') }}
  54. </em>
  55. </p>
  56. </template>
  57. <p class="top-margin">
  58. <NcButton v-if="dirty"
  59. type="primary"
  60. :disabled="loading"
  61. @click="saveChanges">
  62. {{ t('settings', 'Save changes') }}
  63. </NcButton>
  64. </p>
  65. </NcSettingsSection>
  66. </template>
  67. <script>
  68. import axios from '@nextcloud/axios'
  69. import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
  70. import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
  71. import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
  72. import NcSettingsSection from '@nextcloud/vue/dist/Components/NcSettingsSection.js'
  73. import { loadState } from '@nextcloud/initial-state'
  74. import sortedUniq from 'lodash/sortedUniq.js'
  75. import uniq from 'lodash/uniq.js'
  76. import debounce from 'lodash/debounce.js'
  77. import { generateUrl, generateOcsUrl } from '@nextcloud/router'
  78. export default {
  79. name: 'AdminTwoFactor',
  80. components: {
  81. NcSelect,
  82. NcButton,
  83. NcCheckboxRadioSwitch,
  84. NcSettingsSection,
  85. },
  86. data() {
  87. return {
  88. loading: false,
  89. dirty: false,
  90. groups: [],
  91. loadingGroups: false,
  92. twoFactorAdminDoc: loadState('settings', 'two-factor-admin-doc'),
  93. }
  94. },
  95. computed: {
  96. enforced: {
  97. get() {
  98. return this.$store.state.enforced
  99. },
  100. set(val) {
  101. this.dirty = true
  102. this.$store.commit('setEnforced', val)
  103. },
  104. },
  105. enforcedGroups: {
  106. get() {
  107. return this.$store.state.enforcedGroups
  108. },
  109. set(val) {
  110. this.dirty = true
  111. this.$store.commit('setEnforcedGroups', val)
  112. },
  113. },
  114. excludedGroups: {
  115. get() {
  116. return this.$store.state.excludedGroups
  117. },
  118. set(val) {
  119. this.dirty = true
  120. this.$store.commit('setExcludedGroups', val)
  121. },
  122. },
  123. },
  124. mounted() {
  125. // Groups are loaded dynamically, but the assigned ones *should*
  126. // be valid groups, so let's add them as initial state
  127. this.groups = sortedUniq(uniq(this.enforcedGroups.concat(this.excludedGroups)))
  128. // Populate the groups with a first set so the dropdown is not empty
  129. // when opening the page the first time
  130. this.searchGroup('')
  131. },
  132. methods: {
  133. searchGroup: debounce(function(query) {
  134. this.loadingGroups = true
  135. axios.get(generateOcsUrl('cloud/groups?offset=0&search={query}&limit=20', { query }))
  136. .then(res => res.data.ocs)
  137. .then(ocs => ocs.data.groups)
  138. .then(groups => { this.groups = sortedUniq(uniq(this.groups.concat(groups))) })
  139. .catch(err => console.error('could not search groups', err))
  140. .then(() => { this.loadingGroups = false })
  141. }, 500),
  142. saveChanges() {
  143. this.loading = true
  144. const data = {
  145. enforced: this.enforced,
  146. enforcedGroups: this.enforcedGroups,
  147. excludedGroups: this.excludedGroups,
  148. }
  149. axios.put(generateUrl('/settings/api/admin/twofactorauth'), data)
  150. .then(resp => resp.data)
  151. .then(state => {
  152. this.state = state
  153. this.dirty = false
  154. })
  155. .catch(err => {
  156. console.error('could not save changes', err)
  157. })
  158. .then(() => { this.loading = false })
  159. },
  160. },
  161. }
  162. </script>
  163. <style scoped>
  164. .two-factor-loading {
  165. display: inline-block;
  166. vertical-align: sub;
  167. margin-left: -2px;
  168. margin-right: 1px;
  169. }
  170. .top-margin {
  171. margin-top: 0.5rem;
  172. }
  173. </style>