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.

AuthTokenSetupDialogue.vue 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  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 v-if="!adding">
  23. <input v-model="deviceName"
  24. type="text"
  25. :disabled="loading"
  26. :placeholder="t('settings', 'App name')"
  27. @keydown.enter="submit">
  28. <button class="button"
  29. :disabled="loading"
  30. @click="submit">
  31. {{ t('settings', 'Create new app password') }}
  32. </button>
  33. </div>
  34. <div v-else>
  35. {{ t('settings', 'Use the credentials below to configure your app or device.') }}
  36. {{ t('settings', 'For security reasons this password will only be shown once.') }}
  37. <div class="app-password-row">
  38. <span class="app-password-label">{{ t('settings', 'Username') }}</span>
  39. <input :value="loginName"
  40. type="text"
  41. class="monospaced"
  42. readonly="readonly"
  43. @focus="selectInput">
  44. </div>
  45. <div class="app-password-row">
  46. <span class="app-password-label">{{ t('settings', 'Password') }}</span>
  47. <input ref="appPassword"
  48. :value="appPassword"
  49. type="text"
  50. class="monospaced"
  51. readonly="readonly"
  52. @focus="selectInput">
  53. <a ref="clipboardButton"
  54. v-tooltip="copyTooltipOptions"
  55. v-clipboard:copy="appPassword"
  56. v-clipboard:success="onCopyPassword"
  57. v-clipboard:error="onCopyPasswordFailed"
  58. class="icon icon-clippy"
  59. @mouseover="hoveringCopyButton = true"
  60. @mouseleave="hoveringCopyButton = false" />
  61. <button class="button"
  62. @click="reset">
  63. {{ t('settings', 'Done') }}
  64. </button>
  65. </div>
  66. <div class="app-password-row">
  67. <span class="app-password-label" />
  68. <a v-if="!showQR"
  69. @click="showQR = true">
  70. {{ t('settings', 'Show QR code for mobile apps') }}
  71. </a>
  72. <QR v-else
  73. :value="qrUrl" />
  74. </div>
  75. </div>
  76. </template>
  77. <script>
  78. import QR from '@chenfengyuan/vue-qrcode'
  79. import confirmPassword from 'nextcloud-password-confirmation'
  80. export default {
  81. name: 'AuthTokenSetupDialogue',
  82. components: {
  83. QR
  84. },
  85. props: {
  86. add: {
  87. type: Function,
  88. required: true
  89. }
  90. },
  91. data() {
  92. return {
  93. adding: false,
  94. loading: false,
  95. deviceName: '',
  96. appPassword: '',
  97. loginName: '',
  98. passwordCopied: false,
  99. showQR: false,
  100. qrUrl: '',
  101. hoveringCopyButton: false
  102. }
  103. },
  104. computed: {
  105. copyTooltipOptions() {
  106. const base = {
  107. hideOnTargetClick: false,
  108. trigger: 'manual'
  109. }
  110. if (this.passwordCopied) {
  111. return {
  112. ...base,
  113. content: t('core', 'Copied!'),
  114. show: true
  115. }
  116. } else {
  117. return {
  118. ...base,
  119. content: t('core', 'Copy'),
  120. show: this.hoveringCopyButton
  121. }
  122. }
  123. }
  124. },
  125. methods: {
  126. selectInput(e) {
  127. e.currentTarget.select()
  128. },
  129. submit: function() {
  130. confirmPassword()
  131. .then(() => {
  132. this.loading = true
  133. return this.add(this.deviceName)
  134. })
  135. .then(token => {
  136. this.adding = true
  137. this.loginName = token.loginName
  138. this.appPassword = token.token
  139. const server = window.location.protocol + '//' + window.location.host + OC.getRootPath()
  140. this.qrUrl = `nc://login/user:${token.loginName}&password:${token.token}&server:${server}`
  141. this.$nextTick(() => {
  142. this.$refs.appPassword.select()
  143. })
  144. })
  145. .catch(err => {
  146. console.error('could not create a new app password', err)
  147. OC.Notification.showTemporary(t('core', 'Error while creating device token'))
  148. this.reset()
  149. })
  150. },
  151. onCopyPassword() {
  152. this.passwordCopied = true
  153. this.$refs.clipboardButton.blur()
  154. setTimeout(() => { this.passwordCopied = false }, 3000)
  155. },
  156. onCopyPasswordFailed() {
  157. OC.Notification.showTemporary(t('core', 'Could not copy app password. Please copy it manually.'))
  158. },
  159. reset() {
  160. this.adding = false
  161. this.loading = false
  162. this.showQR = false
  163. this.qrUrl = ''
  164. this.deviceName = ''
  165. this.appPassword = ''
  166. this.loginName = ''
  167. }
  168. }
  169. }
  170. </script>
  171. <style lang="scss" scoped>
  172. .app-password-row {
  173. display: table-row;
  174. .icon {
  175. background-size: 16px 16px;
  176. display: inline-block;
  177. position: relative;
  178. top: 3px;
  179. margin-left: 5px;
  180. margin-right: 8px;
  181. }
  182. }
  183. .app-password-label {
  184. display: table-cell;
  185. padding-right: 1em;
  186. text-align: right;
  187. vertical-align: middle;
  188. }
  189. .monospaced {
  190. width: 245px;
  191. font-family: monospace;
  192. }
  193. </style>