Security.vue 6.2 KB

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