Security.vue 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. <template>
  2. <div class="content security-tab">
  3. <div v-if="isPasswordLinked">
  4. <h4 class="section-title">Change password</h4>
  5. <p class="section-description">
  6. You will need to know your previous password.
  7. </p>
  8. <hr class="section-horizontal-rule" />
  9. <p class="control is-expanded margin-top-zero">
  10. <label for="previous-password">Previous password</label>
  11. <input
  12. class="input"
  13. id="previous-password"
  14. type="password"
  15. placeholder="Enter your old password here..."
  16. v-model="previousPassword"
  17. />
  18. </p>
  19. <p id="new-password-again-input" class="control is-expanded">
  20. <label for="new-password">New password</label>
  21. <input
  22. class="input"
  23. id="new-password"
  24. type="password"
  25. placeholder="Enter new password here..."
  26. v-model="validation.newPassword.value"
  27. @keyup.enter="changePassword()"
  28. @blur="onInputBlur('newPassword')"
  29. />
  30. </p>
  31. <transition name="fadein-helpbox">
  32. <input-help-box
  33. v-if="validation.newPassword.entered"
  34. :valid="validation.newPassword.valid"
  35. :message="validation.newPassword.message"
  36. ></input-help-box>
  37. </transition>
  38. <p class="control">
  39. <button
  40. id="change-password-button"
  41. class="button is-success"
  42. @click.prevent="changePassword()"
  43. >
  44. Change password
  45. </button>
  46. </p>
  47. <div class="section-margin-bottom" />
  48. </div>
  49. <div v-if="!isPasswordLinked">
  50. <h4 class="section-title">Add a password</h4>
  51. <p class="section-description">
  52. Add a password, as an alternative to signing in with GitHub.
  53. </p>
  54. <hr class="section-horizontal-rule" />
  55. <router-link to="/set_password" class="button is-default" href="#"
  56. ><i class="material-icons icon-with-button">create</i>Set
  57. Password
  58. </router-link>
  59. <div class="section-margin-bottom" />
  60. </div>
  61. <div v-if="!isGithubLinked">
  62. <h4 class="section-title">Link GitHub</h4>
  63. <p class="section-description">
  64. Link your Musare account with GitHub.
  65. </p>
  66. <hr class="section-horizontal-rule" />
  67. <a class="button is-github" :href="`${apiDomain}/auth/github/link`">
  68. <div class="icon">
  69. <img class="invert" src="/assets/social/github.svg" />
  70. </div>
  71. &nbsp; Link GitHub to account
  72. </a>
  73. <div class="section-margin-bottom" />
  74. </div>
  75. <div v-if="isPasswordLinked && isGithubLinked">
  76. <h4 class="section-title">Remove login methods</h4>
  77. <p class="section-description">
  78. Remove your password as a login method or unlink GitHub.
  79. </p>
  80. <hr class="section-horizontal-rule" />
  81. <div>
  82. <a
  83. v-if="isPasswordLinked"
  84. class="button is-danger"
  85. href="#"
  86. @click.prevent="unlinkPassword()"
  87. >
  88. <i class="material-icons icon-with-button">close</i>
  89. Remove password
  90. </a>
  91. <a
  92. class="button is-danger"
  93. href="#"
  94. @click.prevent="unlinkGitHub()"
  95. >
  96. <i class="material-icons icon-with-button">link_off</i>
  97. Remove GitHub from account
  98. </a>
  99. </div>
  100. <div class="section-margin-bottom" />
  101. </div>
  102. <div>
  103. <h4 class="section-title">Log out everywhere</h4>
  104. <p class="section-description">
  105. Remove all currently logged-in sessions for your account.
  106. </p>
  107. <hr class="section-horizontal-rule" />
  108. <a
  109. class="button is-warning"
  110. href="#"
  111. @click.prevent="removeSessions()"
  112. >
  113. <i class="material-icons icon-with-button">exit_to_app</i>
  114. Log out everywhere
  115. </a>
  116. </div>
  117. </div>
  118. </template>
  119. <script>
  120. import Toast from "toasters";
  121. import { mapGetters, mapState } from "vuex";
  122. import InputHelpBox from "@/components/InputHelpBox.vue";
  123. import validation from "@/validation";
  124. export default {
  125. components: { InputHelpBox },
  126. data() {
  127. return {
  128. apiDomain: "",
  129. previousPassword: "",
  130. validation: {
  131. newPassword: {
  132. value: "",
  133. valid: false,
  134. entered: false,
  135. message: "Please enter a valid password."
  136. }
  137. }
  138. };
  139. },
  140. computed: {
  141. ...mapGetters({
  142. isPasswordLinked: "settings/isPasswordLinked",
  143. isGithubLinked: "settings/isGithubLinked",
  144. socket: "websockets/getSocket"
  145. }),
  146. ...mapState({
  147. userId: state => state.user.auth.userId
  148. })
  149. },
  150. watch: {
  151. // eslint-disable-next-line func-names
  152. "validation.newPassword.value": function(value) {
  153. if (!validation.isLength(value, 6, 200)) {
  154. this.validation.newPassword.message =
  155. "Password must have between 6 and 200 characters.";
  156. this.validation.newPassword.valid = false;
  157. } else if (!validation.regex.password.test(value)) {
  158. this.validation.newPassword.message =
  159. "Invalid password format. Must have one lowercase letter, one uppercase letter, one number and one special character.";
  160. this.validation.newPassword.valid = false;
  161. } else {
  162. this.validation.newPassword.message = "Everything looks great!";
  163. this.validation.newPassword.valid = true;
  164. }
  165. }
  166. },
  167. async mounted() {
  168. this.apiDomain = await lofig.get("apiDomain");
  169. },
  170. methods: {
  171. onInputBlur(inputName) {
  172. this.validation[inputName].entered = true;
  173. },
  174. changePassword() {
  175. const newPassword = this.validation.newPassword.value;
  176. if (this.previousPassword === "")
  177. return new Toast({
  178. content: "Please enter a previous password.",
  179. timeout: 8000
  180. });
  181. if (!this.validation.newPassword.valid)
  182. return new Toast({
  183. content: "Please enter a valid new password.",
  184. timeout: 8000
  185. });
  186. return this.socket.dispatch(
  187. "users.updatePassword",
  188. this.previousPassword,
  189. newPassword,
  190. res => {
  191. if (res.status !== "success")
  192. new Toast({ content: res.message, timeout: 8000 });
  193. else {
  194. this.previousPassword = "";
  195. this.validation.newPassword.value = "";
  196. new Toast({
  197. content: "Successfully changed password.",
  198. timeout: 4000
  199. });
  200. }
  201. }
  202. );
  203. },
  204. unlinkPassword() {
  205. this.socket.dispatch("users.unlinkPassword", res => {
  206. new Toast({ content: res.message, timeout: 8000 });
  207. });
  208. },
  209. unlinkGitHub() {
  210. this.socket.dispatch("users.unlinkGitHub", res => {
  211. new Toast({ content: res.message, timeout: 8000 });
  212. });
  213. },
  214. removeSessions() {
  215. this.socket.dispatch(`users.removeSessions`, this.userId, res => {
  216. new Toast({ content: res.message, timeout: 4000 });
  217. });
  218. }
  219. }
  220. };
  221. </script>
  222. <style lang="scss" scoped>
  223. #change-password-button {
  224. margin-top: 10px;
  225. }
  226. </style>