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 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  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" class="row spacing">
  23. <!-- Port to TextField component when available -->
  24. <input v-model="deviceName"
  25. type="text"
  26. :maxlength="120"
  27. :disabled="loading"
  28. :placeholder="t('settings', 'App name')"
  29. @keydown.enter="submit">
  30. <NcButton :disabled="loading || deviceName.length === 0"
  31. type="primary"
  32. @click="submit">
  33. {{ t('settings', 'Create new app password') }}
  34. </NcButton>
  35. </div>
  36. <div v-else class="spacing">
  37. {{ t('settings', 'Use the credentials below to configure your app or device.') }}
  38. {{ t('settings', 'For security reasons this password will only be shown once.') }}
  39. <div class="app-password-row">
  40. <label for="app-username" class="app-password-label">{{ t('settings', 'Username') }}</label>
  41. <input id="app-username"
  42. :value="loginName"
  43. type="text"
  44. class="monospaced"
  45. readonly="readonly"
  46. @focus="selectInput">
  47. </div>
  48. <div class="app-password-row">
  49. <label for="app-password" class="app-password-label">{{ t('settings', 'Password') }}</label>
  50. <input id="app-password"
  51. ref="appPassword"
  52. :value="appPassword"
  53. type="text"
  54. class="monospaced"
  55. readonly="readonly"
  56. @focus="selectInput">
  57. <NcButton type="tertiary"
  58. :title="copyTooltipOptions"
  59. :aria-label="copyTooltipOptions"
  60. @click="copyPassword">
  61. <template #icon>
  62. <Check v-if="copied" :size="20" />
  63. <ContentCopy v-else :size="20" />
  64. </template>
  65. </NcButton>
  66. <NcButton @click="reset">
  67. {{ t('settings', 'Done') }}
  68. </NcButton>
  69. </div>
  70. <div class="app-password-row">
  71. <span class="app-password-label" />
  72. <a v-if="!showQR"
  73. @click="showQR = true">
  74. {{ t('settings', 'Show QR code for mobile apps') }}
  75. </a>
  76. <QR v-else
  77. :value="qrUrl" />
  78. </div>
  79. </div>
  80. </template>
  81. <script>
  82. import QR from '@chenfengyuan/vue-qrcode'
  83. import { confirmPassword } from '@nextcloud/password-confirmation'
  84. import '@nextcloud/password-confirmation/dist/style.css'
  85. import { showError } from '@nextcloud/dialogs'
  86. import { getRootUrl } from '@nextcloud/router'
  87. import NcButton from '@nextcloud/vue/dist/Components/NcButton'
  88. import Check from 'vue-material-design-icons/Check.vue'
  89. import ContentCopy from 'vue-material-design-icons/ContentCopy.vue'
  90. export default {
  91. name: 'AuthTokenSetupDialogue',
  92. components: {
  93. Check,
  94. ContentCopy,
  95. NcButton,
  96. QR,
  97. },
  98. props: {
  99. add: {
  100. type: Function,
  101. required: true,
  102. },
  103. },
  104. data() {
  105. return {
  106. adding: false,
  107. loading: false,
  108. deviceName: '',
  109. appPassword: '',
  110. loginName: '',
  111. copied: false,
  112. showQR: false,
  113. qrUrl: '',
  114. }
  115. },
  116. computed: {
  117. copyTooltipOptions() {
  118. if (this.copied) {
  119. return t('settings', 'Copied!')
  120. }
  121. return t('settings', 'Copy')
  122. },
  123. },
  124. methods: {
  125. selectInput(e) {
  126. e.currentTarget.select()
  127. },
  128. submit() {
  129. confirmPassword()
  130. .then(() => {
  131. this.loading = true
  132. return this.add(this.deviceName)
  133. })
  134. .then(token => {
  135. this.adding = true
  136. this.loginName = token.loginName
  137. this.appPassword = token.token
  138. const server = window.location.protocol + '//' + window.location.host + getRootUrl()
  139. this.qrUrl = `nc://login/user:${token.loginName}&password:${token.token}&server:${server}`
  140. this.$nextTick(() => {
  141. this.$refs.appPassword.select()
  142. })
  143. })
  144. .catch(err => {
  145. console.error('could not create a new app password', err)
  146. OC.Notification.showTemporary(t('settings', 'Error while creating device token'))
  147. this.reset()
  148. })
  149. },
  150. async copyPassword() {
  151. try {
  152. await navigator.clipboard.writeText(this.appPassword)
  153. this.copied = true
  154. } catch (e) {
  155. this.copied = false
  156. console.error(e)
  157. showError(t('settings', 'Could not copy app password. Please copy it manually.'))
  158. } finally {
  159. setTimeout(() => {
  160. this.copied = false
  161. }, 4000)
  162. }
  163. },
  164. reset() {
  165. this.adding = false
  166. this.loading = false
  167. this.showQR = false
  168. this.qrUrl = ''
  169. this.deviceName = ''
  170. this.appPassword = ''
  171. this.loginName = ''
  172. },
  173. },
  174. }
  175. </script>
  176. <style lang="scss" scoped>
  177. .app-password-row {
  178. display: flex;
  179. align-items: center;
  180. .icon {
  181. background-size: 16px 16px;
  182. display: inline-block;
  183. position: relative;
  184. top: 3px;
  185. margin-left: 5px;
  186. margin-right: 8px;
  187. }
  188. }
  189. .app-password-label {
  190. display: table-cell;
  191. padding-right: 1em;
  192. text-align: right;
  193. vertical-align: middle;
  194. width: 100px;
  195. }
  196. .row input {
  197. height: 44px !important;
  198. padding: 7px 12px;
  199. margin-right: 12px;
  200. width: 200px;
  201. }
  202. .monospaced {
  203. width: 245px;
  204. font-family: monospace;
  205. }
  206. .button-vue{
  207. display:inline-block;
  208. margin: 3px 3px 3px 3px;
  209. }
  210. .row {
  211. display: flex;
  212. align-items: center;
  213. }
  214. .spacing {
  215. padding-top: 16px;
  216. }
  217. </style>