Security.vue 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. <script setup lang="ts">
  2. import { defineAsyncComponent, ref, watch, reactive } from "vue";
  3. import Toast from "toasters";
  4. import { storeToRefs } from "pinia";
  5. import { useConfigStore } from "@/stores/config";
  6. import { useWebsocketsStore } from "@/stores/websockets";
  7. import { useUserAuthStore } from "@/stores/userAuth";
  8. import _validation from "@/validation";
  9. const InputHelpBox = defineAsyncComponent(
  10. () => import("@/components/InputHelpBox.vue")
  11. );
  12. const QuickConfirm = defineAsyncComponent(
  13. () => import("@/components/QuickConfirm.vue")
  14. );
  15. const configStore = useConfigStore();
  16. const { oidcAuthentication } = storeToRefs(configStore);
  17. const userAuthStore = useUserAuthStore();
  18. const { socket } = useWebsocketsStore();
  19. const validation = reactive({
  20. oldPassword: {
  21. value: "",
  22. visible: false
  23. },
  24. newPassword: {
  25. value: "",
  26. visible: false,
  27. valid: false,
  28. entered: false,
  29. message:
  30. "Include at least one lowercase letter, one uppercase letter, one number and one special character."
  31. }
  32. });
  33. const newPassword = ref();
  34. const oldPassword = ref();
  35. const { userId } = storeToRefs(userAuthStore);
  36. const togglePasswordVisibility = refName => {
  37. const ref = refName === "oldPassword" ? oldPassword : newPassword;
  38. if (ref.value.type === "password") {
  39. ref.value.type = "text";
  40. validation[refName].visible = true;
  41. } else {
  42. ref.value.type = "password";
  43. validation[refName].visible = false;
  44. }
  45. };
  46. const onInput = inputName => {
  47. validation[inputName].entered = true;
  48. };
  49. const changePassword = () => {
  50. if (oidcAuthentication.value) return null;
  51. const newPassword = validation.newPassword.value;
  52. if (validation.oldPassword.value === "")
  53. return new Toast("Please enter your previous password.");
  54. if (!validation.newPassword.valid)
  55. return new Toast("Please enter a valid new password.");
  56. return socket.dispatch(
  57. "users.updatePassword",
  58. validation.oldPassword.value,
  59. newPassword,
  60. res => {
  61. if (res.status !== "success") new Toast(res.message);
  62. else {
  63. validation.oldPassword.value = "";
  64. validation.newPassword.value = "";
  65. new Toast("Successfully changed password.");
  66. }
  67. }
  68. );
  69. };
  70. const removeSessions = () => {
  71. socket.dispatch(`users.removeSessions`, userId.value, res => {
  72. new Toast(res.message);
  73. });
  74. };
  75. watch(validation, newValidation => {
  76. const { value } = newValidation.newPassword;
  77. if (!_validation.isLength(value, 6, 200)) {
  78. validation.newPassword.message =
  79. "Password must have between 6 and 200 characters.";
  80. validation.newPassword.valid = false;
  81. } else if (!_validation.regex.password.test(value)) {
  82. validation.newPassword.message =
  83. "Include at least one lowercase letter, one uppercase letter, one number and one special character.";
  84. validation.newPassword.valid = false;
  85. } else {
  86. validation.newPassword.message = "Everything looks great!";
  87. validation.newPassword.valid = true;
  88. }
  89. });
  90. </script>
  91. <template>
  92. <div class="content security-tab">
  93. <template v-if="!oidcAuthentication">
  94. <h4 class="section-title">Change password</h4>
  95. <p class="section-description">
  96. You will need to know your previous password
  97. </p>
  98. <hr class="section-horizontal-rule" />
  99. <p class="control is-expanded margin-top-zero">
  100. <label for="old-password">Previous password</label>
  101. </p>
  102. <div id="password-visibility-container">
  103. <input
  104. class="input"
  105. id="old-password"
  106. ref="oldPassword"
  107. type="password"
  108. placeholder="Enter your old password here..."
  109. v-model="validation.oldPassword.value"
  110. />
  111. <a @click="togglePasswordVisibility('oldPassword')">
  112. <i class="material-icons">
  113. {{
  114. !validation.oldPassword.visible
  115. ? "visibility"
  116. : "visibility_off"
  117. }}
  118. </i>
  119. </a>
  120. </div>
  121. <p class="control is-expanded">
  122. <label for="new-password">New password</label>
  123. </p>
  124. <div id="password-visibility-container">
  125. <input
  126. class="input"
  127. id="new-password"
  128. type="password"
  129. ref="newPassword"
  130. placeholder="Enter new password here..."
  131. v-model="validation.newPassword.value"
  132. @keyup.enter="changePassword()"
  133. @keypress="onInput('newPassword')"
  134. @paste="onInput('newPassword')"
  135. />
  136. <a @click="togglePasswordVisibility('newPassword')">
  137. <i class="material-icons">
  138. {{
  139. !validation.newPassword.visible
  140. ? "visibility"
  141. : "visibility_off"
  142. }}
  143. </i>
  144. </a>
  145. </div>
  146. <transition name="fadein-helpbox">
  147. <input-help-box
  148. :entered="validation.newPassword.entered"
  149. :valid="validation.newPassword.valid"
  150. :message="validation.newPassword.message"
  151. />
  152. </transition>
  153. <p class="control">
  154. <button
  155. id="change-password-button"
  156. class="button is-success"
  157. @click.prevent="changePassword()"
  158. >
  159. Change password
  160. </button>
  161. </p>
  162. <div class="section-margin-bottom" />
  163. </template>
  164. <div>
  165. <h4 class="section-title">Log out everywhere</h4>
  166. <p class="section-description">
  167. Remove all currently logged-in sessions for your account
  168. </p>
  169. <hr class="section-horizontal-rule" />
  170. <div class="row">
  171. <quick-confirm @confirm="removeSessions()">
  172. <a class="button is-warning">
  173. <i class="material-icons icon-with-button"
  174. >exit_to_app</i
  175. >
  176. Logout everywhere
  177. </a>
  178. </quick-confirm>
  179. </div>
  180. </div>
  181. </div>
  182. </template>
  183. <style lang="less" scoped>
  184. #change-password-button {
  185. margin-top: 10px;
  186. }
  187. .control {
  188. margin-bottom: 2px !important;
  189. }
  190. .row {
  191. display: flex;
  192. }
  193. </style>