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.

AuthTokenSection.vue 4.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. <!--
  2. - @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
  3. -
  4. - @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
  5. -
  6. - @license GNU AGPL version 3 or any later version
  7. -
  8. - This program is free software: you can redistribute it and/or modify
  9. - it under the terms of the GNU Affero General Public License as
  10. - published by the Free Software Foundation, either version 3 of the
  11. - License, or (at your option) any later version.
  12. -
  13. - This program is distributed in the hope that it will be useful,
  14. - but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. - GNU Affero General Public License for more details.
  17. -
  18. - You should have received a copy of the GNU Affero General Public License
  19. - along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. -->
  21. <template>
  22. <div id="security" class="section">
  23. <h2>{{ t('settings', 'Devices & sessions') }}</h2>
  24. <p class="settings-hint hidden-when-empty">
  25. {{ t('settings', 'Web, desktop and mobile clients currently logged in to your account.') }}
  26. </p>
  27. <AuthTokenList :tokens="tokens"
  28. @toggleScope="toggleTokenScope"
  29. @rename="rename"
  30. @delete="deleteToken"
  31. @wipe="wipeToken" />
  32. <AuthTokenSetupDialogue v-if="canCreateToken" :add="addNewToken" />
  33. </div>
  34. </template>
  35. <script>
  36. import axios from 'nextcloud-axios'
  37. import confirmPassword from 'nextcloud-password-confirmation'
  38. import AuthTokenList from './AuthTokenList'
  39. import AuthTokenSetupDialogue from './AuthTokenSetupDialogue'
  40. const confirm = () => {
  41. return new Promise(resolve => {
  42. OC.dialogs.confirm(
  43. t('core', 'Do you really want to wipe your data from this device?'),
  44. t('core', 'Confirm wipe'),
  45. resolve,
  46. true
  47. )
  48. })
  49. }
  50. /**
  51. * Tap into a promise without losing the value
  52. * @param {Function} cb the callback
  53. * @returns {any} val the value
  54. */
  55. const tap = cb => val => {
  56. cb(val)
  57. return val
  58. }
  59. export default {
  60. name: 'AuthTokenSection',
  61. components: {
  62. AuthTokenSetupDialogue,
  63. AuthTokenList
  64. },
  65. props: {
  66. tokens: {
  67. type: Array,
  68. required: true
  69. },
  70. canCreateToken: {
  71. type: Boolean,
  72. required: true
  73. }
  74. },
  75. data() {
  76. return {
  77. baseUrl: OC.generateUrl('/settings/personal/authtokens')
  78. }
  79. },
  80. methods: {
  81. addNewToken(name) {
  82. console.debug('creating a new app token', name)
  83. const data = {
  84. name
  85. }
  86. return axios.post(this.baseUrl, data)
  87. .then(resp => resp.data)
  88. .then(tap(() => console.debug('app token created')))
  89. .then(tap(data => this.tokens.push(data.deviceToken)))
  90. .catch(err => {
  91. console.error.bind('could not create app password', err)
  92. OC.Notification.showTemporary(t('core', 'Error while creating device token'))
  93. throw err
  94. })
  95. },
  96. toggleTokenScope(token, scope, value) {
  97. console.debug('updating app token scope', token.id, scope, value)
  98. const oldVal = token.scope[scope]
  99. token.scope[scope] = value
  100. return this.updateToken(token)
  101. .then(tap(() => console.debug('app token scope updated')))
  102. .catch(err => {
  103. console.error.bind('could not update app token scope', err)
  104. OC.Notification.showTemporary(t('core', 'Error while updating device token scope'))
  105. // Restore
  106. token.scope[scope] = oldVal
  107. throw err
  108. })
  109. },
  110. rename(token, newName) {
  111. console.debug('renaming app token', token.id, token.name, newName)
  112. const oldName = token.name
  113. token.name = newName
  114. return this.updateToken(token)
  115. .then(tap(() => console.debug('app token name updated')))
  116. .catch(err => {
  117. console.error.bind('could not update app token name', err)
  118. OC.Notification.showTemporary(t('core', 'Error while updating device token name'))
  119. // Restore
  120. token.name = oldName
  121. })
  122. },
  123. updateToken(token) {
  124. return axios.put(this.baseUrl + '/' + token.id, token)
  125. .then(resp => resp.data)
  126. },
  127. deleteToken(token) {
  128. console.debug('deleting app token', token)
  129. this.tokens = this.tokens.filter(t => t !== token)
  130. return axios.delete(this.baseUrl + '/' + token.id)
  131. .then(resp => resp.data)
  132. .then(tap(() => console.debug('app token deleted')))
  133. .catch(err => {
  134. console.error.bind('could not delete app token', err)
  135. OC.Notification.showTemporary(t('core', 'Error while deleting the token'))
  136. // Restore
  137. this.tokens.push(token)
  138. })
  139. },
  140. async wipeToken(token) {
  141. console.debug('wiping app token', token)
  142. try {
  143. await confirmPassword()
  144. if (!(await confirm())) {
  145. console.debug('wipe aborted by user')
  146. return
  147. }
  148. await axios.post(this.baseUrl + '/wipe/' + token.id)
  149. console.debug('app token marked for wipe')
  150. token.type = 2
  151. } catch (err) {
  152. console.error('could not wipe app token', err)
  153. OC.Notification.showTemporary(t('core', 'Error while wiping the device with the token'))
  154. }
  155. }
  156. }
  157. }
  158. </script>
  159. <style scoped>
  160. </style>