From 8956e68fa16d29d4d0712b7ded7b51d052a80776 Mon Sep 17 00:00:00 2001
From: Maxim Filippov <colixer@gmail.com>
Date: Fri, 27 Sep 2019 18:32:14 +0300
Subject: [PATCH 1/2] `mailerEnabled` must be set to `true` in order to require
 password reset (password reset currently only works via email)

---
 CHANGELOG.md                                     |  4 ++++
 src/api/nodeInfo.js                              | 10 ++++++++++
 src/api/utils.js                                 |  2 +-
 src/lang/en.js                                   |  1 +
 src/store/modules/user.js                        | 11 ++++++++++-
 src/store/modules/users.js                       |  3 ++-
 src/views/users/components/MultipleUsersMenu.vue |  8 ++++++++
 src/views/users/index.vue                        |  8 ++++++++
 8 files changed, 44 insertions(+), 3 deletions(-)
 create mode 100644 src/api/nodeInfo.js

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ac8ad270..be0fb971 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
 ## Unreleased
 
+### Changed
+
+- `mailerEnabled` must be set to `true` in order to require password reset (password reset currently only works via email)
+
 ## [1.2.0] - 2019-09-27
 
 ### Added
diff --git a/src/api/nodeInfo.js b/src/api/nodeInfo.js
new file mode 100644
index 00000000..c67df01b
--- /dev/null
+++ b/src/api/nodeInfo.js
@@ -0,0 +1,10 @@
+import request from '@/utils/request'
+import { baseName } from './utils'
+
+export async function getNodeInfo(authHost) {
+  return await request({
+    baseURL: baseName(authHost),
+    url: `/nodeinfo/2.0.json`,
+    method: 'get'
+  })
+}
diff --git a/src/api/utils.js b/src/api/utils.js
index 1346a204..9f47a1f8 100644
--- a/src/api/utils.js
+++ b/src/api/utils.js
@@ -1,7 +1,7 @@
 const isLocalhost = (instanceName) =>
   instanceName.startsWith('localhost:') || instanceName.startsWith('127.0.0.1:')
 
-export const baseName = (instanceName) => {
+export const baseName = (instanceName = 'localhost') => {
   if (instanceName.match(/https?:\/\//)) {
     return instanceName
   } else {
diff --git a/src/lang/en.js b/src/lang/en.js
index 5d705cdf..8088e441 100644
--- a/src/lang/en.js
+++ b/src/lang/en.js
@@ -215,6 +215,7 @@ export default {
     addTagForMultipleUsersConfirmation: 'Are you sure you want to apply tag to all selected users?',
     removeTagFromMultipleUsersConfirmation: 'Are you sure you want to remove tag from all selected users?',
     requirePasswordResetConfirmation: 'Are you sure you want to require password reset for all selected users?',
+    mailerMustBeEnabled: 'To require user\'s password reset you must enable mailer.',
     ok: 'Okay',
     completed: 'Completed',
     cancel: 'Cancel',
diff --git a/src/store/modules/user.js b/src/store/modules/user.js
index 18ff5419..3ab9339d 100644
--- a/src/store/modules/user.js
+++ b/src/store/modules/user.js
@@ -1,4 +1,5 @@
 import { loginByUsername, getUserInfo } from '@/api/login'
+import { getNodeInfo } from '@/api/nodeInfo'
 import { getToken, setToken, removeToken, getAuthHost, setAuthHost, removeAuthHost } from '@/utils/auth'
 
 const user = {
@@ -15,7 +16,8 @@ const user = {
     roles: [],
     setting: {
       articlePlatform: []
-    }
+    },
+    nodeInfo: {}
   },
 
   mutations: {
@@ -48,6 +50,9 @@ const user = {
     },
     SET_AUTH_HOST: (state, authHost) => {
       state.authHost = authHost
+    },
+    SET_NODE_INFO: (state, nodeInfo) => {
+      state.nodeInfo = nodeInfo
     }
   },
 
@@ -67,7 +72,11 @@ const user = {
         })
       })
     },
+    async GetNodeInfo({ commit, state }) {
+      const nodeInfo = await getNodeInfo(state.authHost)
 
+      commit('SET_NODE_INFO', nodeInfo.data)
+    },
     GetUserInfo({ commit, state }) {
       return new Promise((resolve, reject) => {
         getUserInfo(state.token, state.authHost).then(response => {
diff --git a/src/store/modules/users.js b/src/store/modules/users.js
index 36a7f507..b3d35108 100644
--- a/src/store/modules/users.js
+++ b/src/store/modules/users.js
@@ -98,10 +98,11 @@ const users = {
     async RequirePasswordReset({ commit, getters, state }, user) {
       await requirePasswordReset(user.nickname, getters.authHost, getters.token)
     },
-    async FetchUsers({ commit, state, getters }, { page }) {
+    async FetchUsers({ commit, state, getters, dispatch }, { page }) {
       commit('SET_LOADING', true)
       const filters = Object.keys(state.filters).filter(filter => state.filters[filter]).join()
       const response = await fetchUsers(filters, getters.authHost, getters.token, page)
+      await dispatch('GetNodeInfo')
       loadUsers(commit, page, response.data)
     },
     async GetPasswordResetToken({ commit, state, getters }, nickname) {
diff --git a/src/views/users/components/MultipleUsersMenu.vue b/src/views/users/components/MultipleUsersMenu.vue
index ff016f51..ecd2baf2 100644
--- a/src/views/users/components/MultipleUsersMenu.vue
+++ b/src/views/users/components/MultipleUsersMenu.vue
@@ -273,6 +273,14 @@ export default {
       )
     },
     requirePasswordReset() {
+      const mailerEnabled = this.$store.state.user.nodeInfo.metadata.mailerEnabled
+
+      if (!mailerEnabled) {
+        this.$alert(this.$t('users.mailerMustBeEnabled'), 'Error', { type: 'error' })
+
+        return
+      }
+
       const { requirePasswordReset } = this.mappers()
       this.confirmMessage(
         this.$t('users.requirePasswordResetConfirmation'),
diff --git a/src/views/users/index.vue b/src/views/users/index.vue
index 16160a97..062b23c8 100644
--- a/src/views/users/index.vue
+++ b/src/views/users/index.vue
@@ -265,6 +265,14 @@ export default {
       this.$store.dispatch('GetPasswordResetToken', nickname)
     },
     requirePasswordReset(nickname) {
+      const mailerEnabled = this.$store.state.user.nodeInfo.metadata.mailerEnabled
+
+      if (!mailerEnabled) {
+        this.$alert(this.$t('users.mailerMustBeEnabled'), 'Error', { type: 'error' })
+
+        return
+      }
+
       this.$store.dispatch('RequirePasswordReset', { nickname })
     },
     handleDeactivation({ nickname }) {
-- 
GitLab


From 3daf35d0b0c583837a8e96a765d54d3880aaff99 Mon Sep 17 00:00:00 2001
From: Maxim Filippov <colixer@gmail.com>
Date: Fri, 27 Sep 2019 19:40:25 +0300
Subject: [PATCH 2/2] Fix tests

---
 src/api/__mocks__/nodeInfo.js              | 9 +++++++++
 src/utils/request.js                       | 2 +-
 test/views/login/index.test.js             | 1 +
 test/views/users/index.test.js             | 1 +
 test/views/users/multipleUsersMenu.test.js | 1 +
 test/views/users/usersFilters.test.js      | 1 +
 6 files changed, 14 insertions(+), 1 deletion(-)
 create mode 100644 src/api/__mocks__/nodeInfo.js

diff --git a/src/api/__mocks__/nodeInfo.js b/src/api/__mocks__/nodeInfo.js
new file mode 100644
index 00000000..6daf074e
--- /dev/null
+++ b/src/api/__mocks__/nodeInfo.js
@@ -0,0 +1,9 @@
+export async function getNodeInfo(authHost) {
+  const data = {
+    metadata: {
+      mailerEnabled: true
+    }
+  }
+
+  return Promise.resolve({ data })
+}
diff --git a/src/utils/request.js b/src/utils/request.js
index 271fe591..bbed6501 100644
--- a/src/utils/request.js
+++ b/src/utils/request.js
@@ -10,7 +10,7 @@ const service = axios.create({
 service.interceptors.response.use(
   response => response,
   error => {
-    console.log('Error ' + error)
+    console.log(`Error ${error}`)
     console.log(error.response.data)
 
     // If there's an "error" property in the json, use it
diff --git a/test/views/login/index.test.js b/test/views/login/index.test.js
index b2986b5d..03e45667 100644
--- a/test/views/login/index.test.js
+++ b/test/views/login/index.test.js
@@ -16,6 +16,7 @@ localVue.use(Vuex)
 localVue.use(VueRouter)
 localVue.use(Element)
 
+jest.mock('@/api/nodeInfo')
 jest.mock('@/api/login')
 
 describe('Login', () => {
diff --git a/test/views/users/index.test.js b/test/views/users/index.test.js
index 863186f3..d435e1ce 100644
--- a/test/views/users/index.test.js
+++ b/test/views/users/index.test.js
@@ -14,6 +14,7 @@ const localVue = createLocalVue()
 localVue.use(Vuex)
 localVue.use(Element)
 
+jest.mock('@/api/nodeInfo')
 jest.mock('@/api/users')
 
 describe('Search and filter users', () => {
diff --git a/test/views/users/multipleUsersMenu.test.js b/test/views/users/multipleUsersMenu.test.js
index 355430e1..c92232bf 100644
--- a/test/views/users/multipleUsersMenu.test.js
+++ b/test/views/users/multipleUsersMenu.test.js
@@ -13,6 +13,7 @@ const localVue = createLocalVue()
 localVue.use(Vuex)
 localVue.use(Element)
 
+jest.mock('@/api/nodeInfo')
 jest.mock('@/api/users')
 
 describe('Apply users actions to multiple users', () => {
diff --git a/test/views/users/usersFilters.test.js b/test/views/users/usersFilters.test.js
index 9cf14c66..e56822ca 100644
--- a/test/views/users/usersFilters.test.js
+++ b/test/views/users/usersFilters.test.js
@@ -13,6 +13,7 @@ const localVue = createLocalVue()
 localVue.use(Vuex)
 localVue.use(Element)
 
+jest.mock('@/api/nodeInfo')
 jest.mock('@/api/users')
 
 describe('Filters users', () => {
-- 
GitLab