+
diff --git a/src/client/sagas/accounts/index.js b/src/client/sagas/accounts/index.js
index 05da8ed95..63eaaeb1d 100644
--- a/src/client/sagas/accounts/index.js
+++ b/src/client/sagas/accounts/index.js
@@ -80,6 +80,7 @@ function * saveEditAccount ({ payload }) {
} catch (error) {
let errorText = ''
if (error.response) errorText = error.response.data.error
+ if (errorText.message) errorText = errorText.message
helpers.UI.showSnackbar(`Error: ${errorText}`, true)
Log.error(errorText, error.response || error)
yield put({ type: SAVE_EDIT_ACCOUNT.ERROR, error })
diff --git a/src/controllers/api/apiUtils.js b/src/controllers/api/apiUtils.js
index 244925e7a..6f231ac53 100644
--- a/src/controllers/api/apiUtils.js
+++ b/src/controllers/api/apiUtils.js
@@ -24,7 +24,7 @@ apiUtils.sendApiSuccess = function (res, object) {
}
apiUtils.sendApiError = function (res, errorNum, error) {
- return res.status(errorNum).json({ success: false, error: error })
+ return res.status(errorNum).json({ success: false, error })
}
apiUtils.sendApiError_InvalidPostData = function (res) {
return apiUtils.sendApiError(res, 400, 'Invalid Post Data')
diff --git a/src/controllers/api/v1/users.js b/src/controllers/api/v1/users.js
index e6c2683ab..11f2aae64 100644
--- a/src/controllers/api/v1/users.js
+++ b/src/controllers/api/v1/users.js
@@ -14,12 +14,14 @@
const async = require('async')
const _ = require('lodash')
-const winston = require('winston')
+const winston = require('../../../logger')
const permissions = require('../../../permissions')
const emitter = require('../../../emitter')
const UserSchema = require('../../../models/user')
const groupSchema = require('../../../models/group')
const notificationSchema = require('../../../models/notification')
+const SettingUtil = require('../../../settings/settingsUtil')
+const Chance = require('chance')
const apiUsers = {}
@@ -147,7 +149,7 @@ apiUsers.getWithLimit = function (req, res) {
"error": "Invalid Post Data"
}
*/
-apiUsers.create = function (req, res) {
+apiUsers.create = async function (req, res) {
const response = {}
response.success = true
@@ -174,65 +176,91 @@ apiUsers.create = function (req, res) {
if (postData.aPass !== postData.aPassConfirm)
return res.status(400).json({ success: false, error: 'Invalid Password Match' })
- const Chance = require('chance')
- const chance = new Chance()
+ async.series(
+ [
+ function (next) {
+ SettingUtil.getSettings(function (err, content) {
+ if (err) return next(err)
+ const settings = content.data.settings
+ if (settings.accountsPasswordComplexity.value) {
+ const passwordComplexity = require('../../../settings/passwordComplexity')
+ if (!passwordComplexity.validate(postData.aPass))
+ return next({ message: 'Password does not meet minimum requirements.' })
- const account = new UserSchema({
- username: postData.aUsername,
- password: postData.aPass,
- fullname: postData.aFullname,
- email: postData.aEmail,
- accessToken: chance.hash(),
- role: postData.aRole
- })
+ return next()
+ }
- if (postData.aTitle) {
- account.title = postData.aTitle
- }
+ return next()
+ })
+ },
+ function (next) {
+ const chance = new Chance()
+
+ const account = new UserSchema({
+ username: postData.aUsername,
+ password: postData.aPass,
+ fullname: postData.aFullname,
+ email: postData.aEmail,
+ accessToken: chance.hash(),
+ role: postData.aRole
+ })
- account.save(function (err, a) {
- if (err) {
- response.success = false
- response.error = err
- winston.debug(response)
- return res.status(400).json(response)
- }
+ if (postData.aTitle) {
+ account.title = postData.aTitle
+ }
- a.populate('role', function (err, populatedAccount) {
- if (err) return res.status(500).json({ success: false, error: err })
+ account.save(function (err, a) {
+ if (err) return next(err)
- response.account = populatedAccount.toObject()
- delete response.account.password
+ a.populate('role', function (err, populatedAccount) {
+ if (err) return next(err)
- const groups = []
+ response.account = populatedAccount.toObject()
+ delete response.account.password
- async.each(
- postData.aGrps,
- function (id, done) {
- if (_.isUndefined(id)) return done(null)
- groupSchema.getGroupById(id, function (err, grp) {
- if (err) return done(err)
- if (!grp) return done('Invalid Group (' + id + ') - Group not found. Check Group ID')
+ const groups = []
- grp.addMember(a._id, function (err, success) {
- if (err) return done(err)
+ async.each(
+ postData.aGrps,
+ function (id, done) {
+ if (_.isUndefined(id)) return done()
+ groupSchema.getGroupById(id, function (err, grp) {
+ if (err) return done(err)
+ if (!grp) return done({ message: `Invalid Group (${id}) - Group not found. Check Group ID.` })
+
+ grp.addMember(a._id, function (err, success) {
+ if (err) return done(err)
+
+ grp.save(function (err) {
+ if (err) return done(err)
+ groups.push(grp)
+ done(null, success)
+ })
+ })
+ })
+ },
+ function (e) {
+ if (e) return next(e)
+ response.account.groups = groups
- grp.save(function (err) {
- if (err) return done(err)
- groups.push(grp)
- done(null, success)
- })
- })
+ return next()
+ }
+ )
})
- },
- function (err) {
- if (err) return res.status(400).json({ success: false, error: err })
- response.account.groups = groups
- return res.json(response)
- }
- )
- })
- })
+ })
+ }
+ ],
+ function (e) {
+ if (e) {
+ response.success = false
+ response.error = e
+ winston.debug(response)
+ return res.status(400).json(response)
+ }
+
+ return res.json(response)
+ }
+ )
}
/**
@@ -295,6 +323,20 @@ apiUsers.createPublicAccount = function (req, res) {
return next(null, roleDefault)
})
},
+ function (roleDefault, next) {
+ SettingSchema.getSetting('accountsPasswordComplexity:enable', function (err, passwordComplexitySetting) {
+ if (err) return next(err)
+ if (!passwordComplexitySetting || passwordComplexitySetting.value === true) {
+ const passwordComplexity = require('../../../settings/passwordComplexity')
+ if (!passwordComplexity.validate(postData.user.password))
+ return next({ message: 'Password does not minimum requirements.' })
+
+ return next(null, roleDefault)
+ }
+
+ return next(null, roleDefault)
+ })
+ },
function (roleDefault, next) {
const UserSchema = require('../../../models/user')
user = new UserSchema({
@@ -397,8 +439,20 @@ apiUsers.update = function (req, res) {
obj.groups = [obj.groups]
}
+ let passwordComplexityEnabled = true
+
async.series(
{
+ settings: function (done) {
+ var SettingUtil = require('../../../settings/settingsUtil')
+ SettingUtil.getSettings(function (err, content) {
+ if (err) return done(err)
+ var settings = content.data.settings
+ passwordComplexityEnabled = settings.accountsPasswordComplexity.value
+
+ return done()
+ })
+ },
user: function (done) {
UserSchema.getUserByUsername(username, function (err, user) {
if (err) return done(err)
@@ -413,6 +467,12 @@ apiUsers.update = function (req, res) {
!_.isEmpty(obj.passconfirm)
) {
if (obj.password === obj.passconfirm) {
+ if (passwordComplexityEnabled) {
+ // check Password Complexity
+ const passwordComplexity = require('../../../settings/passwordComplexity')
+ if (!passwordComplexity.validate(obj.password)) return done('Password does not meet requirements')
+ }
+
user.password = obj.password
passwordUpdated = true
}
diff --git a/src/controllers/api/v2/accounts.js b/src/controllers/api/v2/accounts.js
index 05a9dbeca..44ad0f32e 100644
--- a/src/controllers/api/v2/accounts.js
+++ b/src/controllers/api/v2/accounts.js
@@ -14,111 +14,80 @@
const _ = require('lodash')
const async = require('async')
+const winston = require('../../../logger')
const Chance = require('chance')
const apiUtil = require('../apiUtils')
const User = require('../../../models/user')
const Group = require('../../../models/group')
const Team = require('../../../models/team')
const Department = require('../../../models/department')
+const passwordComplexity = require('../../../settings/passwordComplexity')
const accountsApi = {}
-accountsApi.create = function (req, res) {
+accountsApi.create = async function (req, res) {
const postData = req.body
if (!postData) return apiUtil.sendApiError_InvalidPostData(res)
let savedId = null
const chance = new Chance()
- async.series(
- {
- user: function (next) {
- User.create(
- {
- username: postData.username,
- email: postData.email,
- password: postData.password,
- fullname: postData.fullname,
- title: postData.title,
- role: postData.role,
- accessToken: chance.hash()
- },
- function (err, user) {
- if (err) return apiUtil.sendApiError(res, 500, err.message)
- savedId = user._id
-
- return user.populate('role', next)
- }
- )
- },
- groups: function (next) {
- if (!postData.groups) return next(null, [])
-
- Group.getGroups(postData.groups, function (err, groups) {
- if (err) return next(err)
-
- async.each(
- groups,
- function (group, callback) {
- group.addMember(savedId, function (err) {
- if (err) return callback(err)
- group.save(callback)
- })
- },
- function (err) {
- if (err) return next(err)
-
- return next(null, groups)
- }
- )
- })
- },
- teams: function (next) {
- if (!postData.teams) return next()
-
- Team.getTeamsByIds(postData.teams, function (err, teams) {
- if (err) return next(err)
-
- async.each(
- teams,
- function (team, callback) {
- team.addMember(savedId, function () {
- team.save(callback)
- })
- },
- function (err) {
- if (err) return next(err)
+ try {
+ let user = await User.create({
+ username: postData.username,
+ email: postData.email,
+ password: postData.password,
+ fullname: postData.fullname,
+ title: postData.title,
+ role: postData.role,
+ accessToken: chance.hash()
+ })
+
+ savedId = user._id
+
+ const userPopulated = await user.populate('role')
+
+ let groups = []
+ if (postData.groups) {
+ groups = await Group.getGroups(postData.groups)
+ for (const group in groups) {
+ await group.addMember(savedId)
+ await group.save()
+ }
+ }
- return next(null, teams)
- }
- )
- })
- },
- departments: function (next) {
- Department.getUserDepartments(savedId, next)
+ let teams = []
+ if (postData.teams) {
+ const dbTeams = await Team.getTeamsByIds(postData.teams)
+ for (const team of dbTeams) {
+ await team.addMember(savedId)
+ await team.save()
}
- },
- function (err, results) {
- if (err) return apiUtil.sendApiError(res, 500, err.message)
- const user = results.user.toJSON()
- user.groups = results.groups.map(function (g) {
- return { _id: g._id, name: g.name }
- })
+ teams = dbTeams
+ }
- if ((user.role.isAgent || user.role.isAdmin) && results.teams) {
- user.teams = results.teams.map(function (t) {
- return { _id: t._id, name: t.name }
- })
+ const departments = await Department.getUserDepartments(savedId)
+ user = userPopulated.toJSON()
+ user.groups = groups.map(g => {
+ return { _id: g._id, name: g.name }
+ })
- user.departments = results.departments.map(function (d) {
- return { _id: d._id, name: d.name }
- })
- }
+ if ((user.role.isAgent || user.role.isAdmin) && teams.length > 0) {
+ user.teams = teams.map(t => {
+ return { _id: t._id, name: t.name }
+ })
- return apiUtil.sendApiSuccess(res, { account: user })
+ user.departments = departments.map(d => {
+ return { _id: d._id, name: d.name }
+ })
}
- )
+
+ return apiUtil.sendApiSuccess(res, { account: user })
+ } catch (e) {
+ winston.warn(e)
+ return apiUtil.sendApiError(res, 500, e.message)
+ }
}
accountsApi.get = function (req, res) {
@@ -241,183 +210,124 @@ accountsApi.get = function (req, res) {
}
}
-accountsApi.update = function (req, res) {
- var username = req.params.username
- var postData = req.body
+accountsApi.update = async function (req, res) {
+ const username = req.params.username
+ const postData = req.body
if (!username || !postData) return apiUtil.sendApiError_InvalidPostData(res)
let passwordUpdated = false
- async.series(
- {
- user: function (next) {
- User.getByUsername(username, function (err, user) {
- if (err) return next(err)
- if (!user) return next({ message: 'Invalid User' })
-
- postData._id = user._id
-
- if (
- !_.isUndefined(postData.password) &&
- !_.isEmpty(postData.password) &&
- !_.isUndefined(postData.passwordConfirm) &&
- !_.isEmpty(postData.passwordConfirm)
- ) {
- if (postData.password === postData.passwordConfirm) {
- user.password = postData.password
- passwordUpdated = true
- }
- }
-
- if (!_.isUndefined(postData.fullname) && postData.fullname.length > 0) user.fullname = postData.fullname
- if (!_.isUndefined(postData.email) && postData.email.length > 0) user.email = postData.email
- if (!_.isUndefined(postData.title) && postData.title.length > 0) user.title = postData.title
- if (!_.isUndefined(postData.role) && postData.role.length > 0) user.role = postData.role
-
- user.save(function (err, user) {
- if (err) return next(err)
-
- user.populate('role', function (err, populatedUser) {
- if (err) return next(err)
- var resUser = apiUtil.stripUserFields(populatedUser)
-
- return next(null, resUser)
- })
- })
- })
- },
- groups: function (next) {
- if (!postData.groups) return Group.getAllGroupsOfUser(postData._id, next)
-
- var userGroups = []
- Group.getAllGroups(function (err, groups) {
- if (err) return next(err)
- async.each(
- groups,
- function (grp, callback) {
- if (_.includes(postData.groups, grp._id.toString())) {
- if (grp.isMember(postData._id)) {
- userGroups.push(grp)
- return callback()
- }
- grp.addMember(postData._id, function (err, result) {
- if (err) return callback(err)
-
- if (result) {
- grp.save(function (err) {
- if (err) return callback(err)
- userGroups.push(grp)
- return callback()
- })
- } else {
- return callback()
- }
- })
- } else {
- // Remove Member from group
- grp.removeMember(postData._id, function (err, result) {
- if (err) return callback(err)
- if (result) {
- grp.save(function (err) {
- if (err) return callback(err)
-
- return callback()
- })
- } else {
- return callback()
- }
- })
- }
- },
- function (err) {
- if (err) return next(err)
+ try {
+ // SETTINGS
+ const SettingsUtil = require('../../../settings/settingsUtil')
+ const settingsContent = await SettingsUtil.getSettings()
+ const settings = settingsContent.data.settings
+ const passwordComplexityEnabled = settings.accountsPasswordComplexity.value
+
+ // USER
+ let user = await User.getByUsername(username)
+ if (!user) throw new Error('Invalid User')
+
+ postData._id = user._id
+ if (
+ !_.isUndefined(postData.password) &&
+ !_.isEmpty(postData.password) &&
+ !_.isUndefined(postData.passwordConfirm) &&
+ !_.isEmpty(postData.passwordConfirm)
+ ) {
+ if (postData.password === postData.passwordConfirm) {
+ if (passwordComplexityEnabled) {
+ if (!passwordComplexity.validate(postData.password)) throw new Error('Password does not meet requirements')
+ }
+
+ user.password = postData.password
+ passwordUpdated = true
+ } else throw new Error('Password and Confirm Password do not match.')
+ }
- return next(null, userGroups)
+ if (!_.isUndefined(postData.fullname) && postData.fullname.length > 0) user.fullname = postData.fullname
+ if (!_.isUndefined(postData.email) && postData.email.length > 0) user.email = postData.email
+ if (!_.isUndefined(postData.title) && postData.title.length > 0) user.title = postData.title
+ if (!_.isUndefined(postData.role) && postData.role.length > 0) user.role = postData.role
+
+ user = await user.save()
+ const populatedUser = await user.populate('role')
+ const resUser = apiUtil.stripUserFields(populatedUser)
+
+ // GROUPS
+ let groups = []
+ if (!postData.groups) groups = await Group.getAllGroupsOfUser(postData._id)
+ else {
+ const allGroups = await Group.getAllGroups()
+ for (const g of allGroups) {
+ if (_.includes(postData.groups, g._id.toString())) {
+ if (g.isMember(postData._id)) {
+ groups.push(g)
+ } else {
+ const result = await g.addMember(postData._id)
+ if (result) {
+ await g.save()
+ groups.push(g)
}
- )
- })
- },
- teams: function (next) {
- if (!postData.teams) return Team.getTeamsOfUser(postData._id, next)
-
- var userTeams = []
- Team.getTeams(function (err, teams) {
- if (err) return next(err)
- async.each(
- teams,
- function (team, callback) {
- if (_.includes(postData.teams, team._id.toString())) {
- if (team.isMember(postData._id)) {
- userTeams.push(team)
- return callback()
- }
- team.addMember(postData._id, function (err, result) {
- if (err) return callback(err)
-
- if (result) {
- team.save(function (err) {
- if (err) return callback(err)
- userTeams.push(team)
- return callback()
- })
- } else {
- return callback()
- }
- })
- } else {
- // Remove Member from group
- team.removeMember(postData._id, function (err, result) {
- if (err) return callback(err)
- if (result) {
- team.save(function (err) {
- if (err) return callback(err)
-
- return callback()
- })
- } else {
- return callback()
- }
- })
- }
- },
- function (err) {
- if (err) return next(err)
+ }
+ } else {
+ const result = await g.removeMember(postData._id)
+ if (result) await g.save()
+ }
+ }
+ }
- return next(null, userTeams)
+ // TEAMS
+ let teams = []
+ if (!postData.teams) {
+ teams = await Team.getTeamsOfUser(postData._id)
+ } else {
+ const allTeams = await Team.getTeams()
+ for (const t of allTeams) {
+ if (_.includes(postData.teams, t._id.toString())) {
+ if (t.isMember(postData._id)) teams.push(t)
+ else {
+ const result = await t.addMember(postData._id)
+ if (result) {
+ await t.save()
+ teams.push(t)
}
- )
- })
- },
- departments: function (next) {
- Department.getUserDepartments(postData._id, next)
+ }
+ } else {
+ const result = await t.removeMember(postData._id)
+ if (result) await t.save()
+ }
}
- },
- async function (err, results) {
- if (err) return apiUtil.sendApiError(res, 500, err.message)
+ }
- var user = results.user.toJSON()
- user.groups = results.groups.map(function (g) {
- return { _id: g._id, name: g.name }
- })
+ // DEPARTMENTS
+ const departments = await Department.getUserDepartments(postData._id)
- if ((user.role.isAgent || user.role.isAdmin) && results.teams) {
- user.teams = results.teams.map(function (t) {
- return { _id: t._id, name: t.name }
- })
+ user = resUser.toJSON()
+ user.groups = groups.map(g => {
+ return { _id: g._id, name: g.name }
+ })
- user.departments = results.departments.map(function (d) {
- return { _id: d._id, name: d.name }
- })
- }
+ if ((user.role.isAgent || user.role.isAdmin) && teams.length > 0) {
+ user.teams = teams.map(t => {
+ return { _id: t._id, name: t.name }
+ })
- if (passwordUpdated) {
- const Session = require('../../../models/session')
- await Session.destroy(user._id)
- }
+ user.departments = departments.map(d => {
+ return { _id: d._id, name: d.name }
+ })
+ }
- return apiUtil.sendApiSuccess(res, { user: user })
+ if (passwordUpdated) {
+ const Session = require('../../../models/session')
+ await Session.destroy(user._id)
}
- )
+
+ return apiUtil.sendApiSuccess(res, { user })
+ } catch (e) {
+ const error = { name: e.name, message: e.message }
+ return apiUtil.sendApiError(res, 400, error)
+ }
}
module.exports = accountsApi
diff --git a/src/controllers/settings.js b/src/controllers/settings.js
index ea7002904..f810527ee 100644
--- a/src/controllers/settings.js
+++ b/src/controllers/settings.js
@@ -76,6 +76,14 @@ settingsController.general = function (req, res) {
renderView(res, content)
}
+settingsController.accounts = function (req, res) {
+ if (!checkPerms(req, 'settings:view')) return res.redirect('/')
+
+ const content = initViewContent('accounts', req)
+
+ renderView(res, content)
+}
+
settingsController.appearance = function (req, res) {
if (!checkPerms(req, 'settings:view')) return res.redirect('/')
diff --git a/src/helpers/viewdata/index.js b/src/helpers/viewdata/index.js
index 19e45db0b..e11e0748c 100644
--- a/src/helpers/viewdata/index.js
+++ b/src/helpers/viewdata/index.js
@@ -17,6 +17,7 @@ const _ = require('lodash')
const winston = require('../../logger')
const moment = require('moment')
const settingSchema = require('../../models/setting')
+const settingsUtil = require('../../settings/settingsUtil')
const viewController = {}
const viewdata = {}
@@ -356,7 +357,6 @@ viewController.getData = function (request, cb) {
})
},
function (callback) {
- const settingsUtil = require('../../settings/settingsUtil')
settingsUtil.getSettings(function (err, res) {
if (err) return callback(err)
@@ -365,6 +365,15 @@ viewController.getData = function (request, cb) {
return callback()
})
},
+ function (callback) {
+ settingsUtil.getSettings(function (err, res) {
+ if (err) return callback(err)
+
+ viewdata.accountsPasswordComplexity = res.data.settings.accountsPasswordComplexity.value
+
+ return callback()
+ })
+ },
function (callback) {
viewController.getPluginsInfo(request, function (err, data) {
if (err) return callback(err)
diff --git a/src/models/department.js b/src/models/department.js
index 1424208bf..0912fcb68 100644
--- a/src/models/department.js
+++ b/src/models/department.js
@@ -48,16 +48,23 @@ departmentSchema.statics.getDepartmentsByTeam = function (teamIds, callback) {
.exec(callback)
}
-departmentSchema.statics.getUserDepartments = function (userId, callback) {
- var self = this
-
- Teams.getTeamsOfUser(userId, function (err, teams) {
- if (err) return callback(err)
+departmentSchema.statics.getUserDepartments = async function (userId, callback) {
+ const self = this
+ return new Promise((resolve, reject) => {
+ ;(async () => {
+ try {
+ const teams = await Teams.getTeamsOfUser(userId)
+ const exec = self.model(COLLECTION).find({ teams: { $in: teams } })
+ if (typeof callback === 'function') {
+ return exec.exec(callback)
+ }
- return self
- .model(COLLECTION)
- .find({ teams: { $in: teams } })
- .exec(callback)
+ const departments = await exec.exec()
+ return resolve(departments)
+ } catch (e) {
+ return reject(e)
+ }
+ })()
})
}
diff --git a/src/models/group.js b/src/models/group.js
index 8207b1371..d54110c95 100644
--- a/src/models/group.js
+++ b/src/models/group.js
@@ -165,14 +165,33 @@ groupSchema.statics.getAllPublicGroups = function (callback) {
return q.exec(callback)
}
-groupSchema.statics.getGroups = function (groupIds, callback) {
- if (_.isUndefined(groupIds)) return callback('Invalid Array of Group IDs - GroupSchema.GetGroups()')
-
- this.model(COLLECTION)
- .find({ _id: { $in: groupIds } })
- .populate('members', '_id username fullname email role preferences image title deleted')
- .sort('name')
- .exec(callback)
+groupSchema.statics.getGroups = async function (groupIds, callback) {
+ return new Promise((resolve, reject) => {
+ ;(async () => {
+ if (_.isUndefined(groupIds)) {
+ if (typeof callback === 'function') return callback('Invalid Array of Group IDs - GroupSchema.GetGroups()')
+ return reject(new Error('Invalid Array of Group IDs - GroupSchema.GetGroups()'))
+ }
+
+ try {
+ const exec = this.model(COLLECTION)
+ .find({ _id: { $in: groupIds } })
+ .populate('members', '_id username fullname email role preferences image title deleted')
+ .sort('name')
+
+ if (typeof callback === 'function') {
+ return exec.exec(callback)
+ }
+
+ const groups = await exec.exec()
+
+ return resolve(groups)
+ } catch (e) {
+ if (typeof callback === 'function') return callback(e)
+ return reject(e)
+ }
+ })()
+ })
}
groupSchema.statics.getAllGroupsOfUser = function (userId, callback) {
diff --git a/src/models/team.js b/src/models/team.js
index 8c56349c3..43527e1e3 100644
--- a/src/models/team.js
+++ b/src/models/team.js
@@ -45,17 +45,22 @@ teamSchema.pre('save', function (next) {
return next()
})
-teamSchema.methods.addMember = function (memberId, callback) {
- if (_.isUndefined(memberId)) return callback('Invalid MemberId - TeamSchema.AddMember()')
+teamSchema.methods.addMember = async function (memberId, callback) {
+ return new Promise((resolve, reject) => {
+ ;(async () => {
+ if (_.isUndefined(memberId)) {
+ if (typeof callback === 'function') return callback({ message: 'Invalid MemberId - TeamSchema.AddMember()' })
+ return reject(new Error('Invalid MemberId - TeamSchema.AddMember()'))
+ }
- if (this.members === null) this.members = []
+ if (this.members === null) this.members = []
- if (isMember(this.members, memberId)) return callback(null, false)
+ this.members.push(memberId)
+ this.members = _.uniq(this.members)
- this.members.push(memberId)
- this.members = _.uniq(this.members)
-
- return callback(null, true)
+ return resolve(true)
+ })()
+ })
}
teamSchema.methods.removeMember = function (memberId, callback) {
diff --git a/src/public/js/angularjs/controllers/profile.js b/src/public/js/angularjs/controllers/profile.js
index 2396cd364..704ec532c 100644
--- a/src/public/js/angularjs/controllers/profile.js
+++ b/src/public/js/angularjs/controllers/profile.js
@@ -91,8 +91,13 @@ define([
})
})
.error(function (e) {
- $log.log('[trudesk:profile:updateUser] - ' + e.error.message)
- helpers.UI.showSnackbar('Error ' + e.error.message, true)
+ if (e.error.message) {
+ $log.log('[trudesk:profile:updateUser] - ' + e.error.message)
+ helpers.UI.showSnackbar('Error ' + e.error.message, true)
+ } else {
+ $log.log('[trudesk:profile:updateUser] - ' + e.error)
+ helpers.UI.showSnackbar('Error: ' + e.error, true)
+ }
})
}
diff --git a/src/public/js/vendor/formvalidator/jquery.form-validator.js b/src/public/js/vendor/formvalidator/jquery.form-validator.js
index 782074f9b..146234286 100644
--- a/src/public/js/vendor/formvalidator/jquery.form-validator.js
+++ b/src/public/js/vendor/formvalidator/jquery.form-validator.js
@@ -2061,6 +2061,13 @@
// errorMessageKey: '' // not used
})
+ $.formUtils.addValidator({
+ name: 'none',
+ validatorFunction: function(value, $el, config, language, $form) {
+ return true
+ }
+ })
+
$.formUtils.addValidator({
name: 'confirmation',
validatorFunction: function (value, $el, config, language, $form) {
diff --git a/src/routes/index.js b/src/routes/index.js
index 74d1b0f23..54a84c277 100644
--- a/src/routes/index.js
+++ b/src/routes/index.js
@@ -283,6 +283,7 @@ function mainRoutes (router, middleware, controllers) {
router.get('/settings', middleware.redirectToLogin, middleware.loadCommonData, controllers.settings.general)
router.get('/settings/general', middleware.redirectToLogin, middleware.loadCommonData, controllers.settings.general)
+ router.get('/settings/accounts', middleware.redirectToLogin, middleware.loadCommonData, controllers.settings.accounts)
router.get(
'/settings/appearance',
middleware.redirectToLogin,
diff --git a/src/settings/passwordComplexity.js b/src/settings/passwordComplexity.js
new file mode 100644
index 000000000..d14ea39bd
--- /dev/null
+++ b/src/settings/passwordComplexity.js
@@ -0,0 +1,50 @@
+/*
+ * . .o8 oooo
+ * .o8 "888 `888
+ * .o888oo oooo d8b oooo oooo .oooo888 .ooooo. .oooo.o 888 oooo
+ * 888 `888""8P `888 `888 d88' `888 d88' `88b d88( "8 888 .8P'
+ * 888 888 888 888 888 888 888ooo888 `"Y88b. 888888.
+ * 888 . 888 888 888 888 888 888 .o o. )88b 888 `88b.
+ * "888" d888b `V88V"V8P' `Y8bod88P" `Y8bod8P' 8""888P' o888o o888o
+ * ========================================================================
+ * Author: Chris Brame
+ * Updated: 5/17/22 6:33 PM
+ * Copyright (c) 2014-2022. All rights reserved.
+ */
+
+const { passwordStrength } = require('check-password-strength')
+const passwordComplexityOptions = [
+ {
+ id: 0,
+ value: 'Too weak',
+ minDiversity: 0,
+ minLength: 0
+ },
+ {
+ id: 1,
+ value: 'Weak',
+ minDiversity: 2,
+ minLength: 6
+ },
+ {
+ id: 2,
+ value: 'Medium',
+ minDiversity: 3,
+ minLength: 8
+ },
+ {
+ id: 3,
+ value: 'Strong',
+ minDiversity: 4,
+ minLength: 10
+ }
+]
+
+const passwordComplexity = {}
+
+passwordComplexity.validate = password => {
+ const response = passwordStrength(password, passwordComplexityOptions)
+ return !(response.id === 0 || response.id === 1)
+}
+
+module.exports = passwordComplexity
diff --git a/src/settings/settingsUtil.js b/src/settings/settingsUtil.js
index af6674ce9..b42728c12 100644
--- a/src/settings/settingsUtil.js
+++ b/src/settings/settingsUtil.js
@@ -122,6 +122,8 @@ util.getSettings = async callback => {
s.maintenanceMode = parseSetting(settings, 'maintenanceMode:enable', false)
+ s.accountsPasswordComplexity = parseSetting(settings, 'accountsPasswordComplexity:enable', true)
+
const types = await ticketTypeSchema.getTypes()
content.data.ticketTypes = _.sortBy(types, o => o.name)
diff --git a/yarn.lock b/yarn.lock
index 7d6b68cba..7e10022fe 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3878,6 +3878,11 @@ check-error@^1.0.2:
resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=
+check-password-strength@2.0.5:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/check-password-strength/-/check-password-strength-2.0.5.tgz#bb10da01d24bd69e5e629c5cea2a6b729e5061af"
+ integrity sha512-b61T/+4OIGWSMRxJUsYOY44Cf9w7orIt2OQmF/WgH16qbJKIT1jG3XHx3jP+o090eH7rq13DRleKgXCiROBzMQ==
+
cheerio-select@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.5.0.tgz#faf3daeb31b17c5e1a9dabcee288aaf8aafa5823"