CVE-2022-1775: Weak Password Requirements (CWE-521)
value: Weak Password Requirements in GitHub repository polonel/trudesk prior to 1.2.2.
CWE description: The product does not require that users should have strong passwords, which makes it easier for attackers to compromise user accounts.
CVSS v3 base score: 9.8
Published: 2022-05-20T23:15Z
Repository: https://github.com/polonel/trudesk
Language: JavaScript
Files changed: package.json, src/client/components/Nav/Sidebar/index.jsx, src/client/containers/Modals/CreateAccountModal.jsx, src/client/containers/Settings/Accounts/index.jsx, src/client/containers/Settings/General/index.jsx, src/client/containers/Settings/SettingsContainer.jsx, src/client/sagas/accounts/index.js, src/controllers/api/apiUtils.js, src/controllers/api/v1/users.js, src/controllers/api/v2/accounts.js, src/controllers/settings.js, src/helpers/viewdata/index.js, src/models/department.js, src/models/group.js, src/models/team.js, src/public/js/angularjs/controllers/profile.js, src/public/js/vendor/formvalidator/jquery.form-validator.js, src/routes/index.js, src/settings/passwordComplexity.js, src/settings/settingsUtil.js, yarn.lock
Commit date: 2022-05-18 00:18:45 -0400
Commit message: refactor(accounts): password security enhancements
Diff: diff --git a/package.json b/package.json
index cc6e7ed75..213fc7c2c 100644
--- a/package.json
+++ b/package.json
@@ -30,10 +30,11 @@
     "axios": "0.26.0",
     "bcrypt": "5.0.1",
     "body-parser": "1.19.2",
     "busboy": "1.4.0",
     "chance": "1.1.8",
+    "check-password-strength": "2.0.5",
     "cheerio": "1.0.0-rc.10",
     "clone": "2.1.2",
     "clsx": "1.1.1",
     "connect-flash": "0.1.1",
     "connect-mongo": "4.6.0",
diff --git a/src/client/components/Nav/Sidebar/index.jsx b/src/client/components/Nav/Sidebar/index.jsx
index 3fdd2bc93..d970252c7 100644
--- a/src/client/components/Nav/Sidebar/index.jsx
+++ b/src/client/components/Nav/Sidebar/index.jsx
@@ -278,10 +278,16 @@ class Sidebar extends React.Component {
                     text='General'
                     icon='tune'
                     href='/settings'
                     active={activeSubItem === 'settings-general'}
                   />
+                  <SubmenuItem
+                    text='Accounts'
+                    icon='tune'
+                    href='/settings/accounts'
+                    active={activeSubItem === 'settings-accounts'}
+                  />
                   <SubmenuItem
                     text='Appearance'
                     icon='style'
                     href='/settings/appearance'
                     active={activeSubItem === 'settings-appearance'}
diff --git a/src/client/containers/Modals/CreateAccountModal.jsx b/src/client/containers/Modals/CreateAccountModal.jsx
index dde731bf5..7df0f8cd2 100644
--- a/src/client/containers/Modals/CreateAccountModal.jsx
+++ b/src/client/containers/Modals/CreateAccountModal.jsx
@@ -201,13 +201,13 @@ class CreateAccountModal extends React.Component {
                   type='password'
                   className={'md-input'}
                   name={'password_confirmation'}
                   value={this.password}
                   onChange={e => this.onInputChanged(e, 'password')}
-                  data-validation={'length'}
-                  data-validation-length={'min6'}
-                  data-validation-error-msg={'Password must contain at least 6 characters.'}
+                  data-validation={this.props.common.accountsPasswordComplexity ? 'length' : 'none'}
+                  data-validation-length={'min8'}
+                  data-validation-error-msg={'Password must contain at least 8 characters.'}
                 />
               </div>
               <div className='uk-float-left uk-width-1-2'>
                 <label className={'uk-form-label'}>Confirm Password</label>
                 <input
diff --git a/src/client/containers/Settings/Accounts/index.jsx b/src/client/containers/Settings/Accounts/index.jsx
new file mode 100644
index 000000000..485e24f80
--- /dev/null
+++ b/src/client/containers/Settings/Accounts/index.jsx
@@ -0,0 +1,146 @@
+/*
+ *       .                             .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 2:20 PM
+ *  Copyright (c) 2014-2022. All rights reserved.
+ */
+
+import React from 'react'
+import PropTypes from 'prop-types'
+import { connect } from 'react-redux'
+import { updateSetting, updateMultipleSettings } from 'actions/settings'
+
+import Button from 'components/Button'
+import SettingItem from 'components/Settings/SettingItem'
+
+import helpers from 'lib/helpers'
+import axios from 'axios'
+import Log from '../../../logger'
+import EnableSwitch from 'components/Settings/EnableSwitch'
+import { observer } from 'mobx-react'
+import { makeObservable, observable } from 'mobx'
+import UIKit from 'uikit'
+
+@observer
+class AccountsSettingsContainer extends React.Component {
+  @observable passwordComplexityEnabled = false
+  @observable allowUserRegistrationEnabled = false
+
+  constructor (props) {
+    super(props)
+
+    makeObservable(this)
+
+    this.state = {
+      restarting: false
+    }
+
+    this.restartServer = this.restartServer.bind(this)
+  }
+
+  componentDidMount () {
+    // helpers.UI.inputs()
+  }
+
+  componentDidUpdate (prevProps) {
+    // helpers.UI.reRenderInputs()
+    if (prevProps.settings !== this.props.settings) {
+      if (this.passwordComplexityEnabled !== this.getSetting('accountsPasswordComplexity'))
+        this.passwordComplexityEnabled = this.getSetting('accountsPasswordComplexity')
+      if (this.allowUserRegistrationEnabled !== this.getSetting('allowUserRegistration'))
+        this.allowUserRegistrationEnabled = this.getSetting('allowUserRegistration')
+    }
+  }
+
+  restartServer () {
+    this.setState({ restarting: true })
+
+    const token = document.querySelector('meta[name="csrf-token"]').getAttribute('content')
+    axios
+      .post(
+        '/api/v1/admin/restart',
+        {},
+        {
+          headers: {
+            'CSRF-TOKEN': token
+          }
+        }
+      )
+      .catch(error => {
+        helpers.hideLoader()
+        Log.error(error.responseText)
+        Log.error('Unable to restart server. Server must run under PM2 and Account must have admin rights.')
+        helpers.UI.showSnackbar('Unable to restart server. Are you an Administrator?', true)
+      })
+      .then(() => {
+        this.setState({ restarting: false })
+      })
+  }
+
+  getSetting (stateName) {
+    return this.props.settings.getIn(['settings', stateName, 'value'])
+      ? this.props.settings.getIn(['settings', stateName, 'value'])
+      : ''
+  }
+
+  updateSetting (stateName, name, value) {
+    this.props.updateSetting({ stateName, name, value })
+  }
+
+  render () {
+    const { active } = this.props
+    return (
+      <div className={active ? 'active' : 'hide'}>
+        <SettingItem
+          title='Allow User Registration'
+          subtitle='Allow users to create accounts on the login screen.'
+          component={
+            <EnableSwitch
+              stateName='allowUserRegistration'
+              label='Enable'
+              checked={this.allowUserRegistrationEnabled}
+              onChange={e => {
+                this.updateSetting('allowUserRegistration', 'allowUserRegistration:enable', e.target.checked)
+              }}
+            />
+          }
+        />
+        <SettingItem
+          title={'Password Complexity'}
+          subtitle={'Require users passwords to meet minimum password complexity'}
+          tooltip={'Minimum 8 characters with uppercase and numeric.'}
+          component={
+            <EnableSwitch
+              stateName={'accountsPasswordComplexity'}
+              label={'Enable'}
+              checked={this.passwordComplexityEnabled}
+              onChange={e => {
+                this.updateSetting('accountsPasswordComplexity', 'accountsPasswordComplexity:enable', e.target.checked)
+              }}
+            />
+          }
+        />
+      </div>
+    )
+  }
+}
+
+AccountsSettingsContainer.propTypes = {
+  active: PropTypes.bool.isRequired,
+  updateSetting: PropTypes.func.isRequired,
+  updateMultipleSettings: PropTypes.func.isRequired,
+  settings: PropTypes.object.isRequired
+}
+
+const mapStateToProps = state => ({
+  settings: state.settings.settings
+})
+
+export default connect(mapStateToProps, { updateSetting, updateMultipleSettings })(AccountsSettingsContainer)
diff --git a/src/client/containers/Settings/General/index.jsx b/src/client/containers/Settings/General/index.jsx
index b74ea8433..8e753f167 100644
--- a/src/client/containers/Settings/General/index.jsx
+++ b/src/client/containers/Settings/General/index.jsx
@@ -92,21 +92,10 @@ class GeneralSettings extends React.Component {
         }}
         showTextbox={true}
       />
     )
 
-    const AllowUserRegistration = (
-      <EnableSwitch
-        stateName='allowUserRegistration'
-        label='Enable'
-        checked={this.getSettingsValue('allowUserRegistration')}
-        onChange={e => {
-          this.updateSetting('allowUserRegistration', 'allowUserRegistration:enable', e.target.checked)
-        }}
-      />
-    )
-
     return (
       <div className={active ? 'active' : 'hide'}>
         <SettingItem
           title='Site Title'
           subtitle={
@@ -182,15 +171,10 @@ class GeneralSettings extends React.Component {
                 }
               />
             </ZoneBox>
           </Zone>
         </SettingItem>
-        <SettingItem
-          title='Allow User Registration'
-          subtitle='Allow users to create accounts on the login screen.'
-          component={AllowUserRegistration}
-        />
       </div>
     )
   }
 }
 
diff --git a/src/client/containers/Settings/SettingsContainer.jsx b/src/client/containers/Settings/SettingsContainer.jsx
index 6a219f345..3ccc66e88 100644
--- a/src/client/containers/Settings/SettingsContainer.jsx
+++ b/src/client/containers/Settings/SettingsContainer.jsx
@@ -19,10 +19,11 @@ import { connect } from 'react-redux'
 import { fetchSettings } from 'actions/settings'
 
 import Menu from 'components/Settings/Menu'
 import MenuItem from 'components/Settings/MenuItem'
 import GeneralSettings from './General'
+import AccountsSettings from './Accounts'
 import AppearanceSettings from './Appearance'
 import PermissionsSettingsContainer from './Permissions'
 import TicketsSettings from './Tickets'
 import MailerSettingsContainer from './Mailer'
 import ElasticsearchSettingsContainer from './Elasticsearch'
@@ -87,10 +88,17 @@ class SettingsContainer extends React.Component {
                 active={this.state.activeCategory === 'settings-general'}
                 onClick={e => {
                   this.onMenuItemClick(e, 'general')
                 }}
               />
+              <MenuItem
+                title='Accounts'
+                active={this.state.activeCategory === 'settings-accounts'}
+                onClick={e => {
+                  this.onMenuItemClick(e, 'accounts')
+                }}
+              />
               <MenuItem
                 title='Appearance'
                 active={this.state.activeCategory === 'settings-appearance'}
                 onClick={e => {
                   this.onMenuItemClick(e, 'appearance')
@@ -154,10 +162,11 @@ class SettingsContainer extends React.Component {
             style={{ borderTop: 'none', height: '69px' }}
           />
           <div className='page-wrapper full-height scrollable no-overflow-x' ref={i => (this.page = i)}>
             <div className='settings-wrap'>
               <GeneralSettings active={this.state.activeCategory === 'settings-general'} />
+              <AccountsSettings active={this.state.activeCategory === 'settings-accounts'} />
               <AppearanceSettings active={this.state.activeCategory === 'settings-appearance'} />
               <PermissionsSettingsContainer active={this.state.activeCategory === 'settings-permissions'} />
               <TicketsSettings active={this.state.activeCategory === 'settings-tickets'} />
               <MailerSettingsContainer active={this.state.activeCategory === 'settings-mailer'} />
               <ElasticsearchSettingsContainer active={this.state.activeCategory === 'settings-elasticsearch'} />
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
@@ -78,10 +78,11 @@ function * saveEditAccount ({ payload }) {
     yield put({ type: HIDE_MODAL.ACTION })
     helpers.UI.showSnackbar('Account updated successfully')
   } 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
@@ -22,11 +22,11 @@ apiUtils.sendApiSuccess = function (res, object) {
 
   return res.json(resObject)
 }
 
 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
@@ -12,16 +12,18 @@
  *  Copyright (c) 2014-2019. All rights reserved.
  */
 
 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 = {}
 
 /**
  * @api {get} /api/v1/users Gets users with query string
@@ -145,11 +147,11 @@ apiUsers.getWithLimit = function (req, res) {
  *      HTTP/1.1 400 Bad Request
  {
      "error": "Invalid Post Data"
  }
  */
-apiUsers.create = function (req, res) {
+apiUsers.create = async function (req, res) {
   const response = {}
   response.success = true
 
   const postData = req.body
 
@@ -172,69 +174,95 @@ 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)
+    }
+  )
 }
 
 /**
  * @api {post} /api/v1/public/account/create Create Public Account
  * @apiName createPublicAccount
@@ -293,10 +321,24 @@ 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({
           username: postData.user.email,
           password: postData.user.password,
@@ -395,12 +437,24 @@ apiUsers.update = function (req, res) {
     obj.groups = []
   } else if (!_.isArray(obj.groups)) {
     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)
           if (!user) return done('Invalid User Object')
 
@@ -411,10 +465,16 @@ apiUsers.update = function (req, res) {
             !_.isEmpty(obj.password) &&
             !_.isUndefined(obj.passconfirm) &&
             !_.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
@@ -12,115 +12,84 @@
  *  Copyright (c) 2014-2019. All rights reserved.
  */
 
 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) {
   const query = req.query
   const type = query.type || 'customers'
@@ -239,185 +208,126 @@ accountsApi.get = function (req, res) {
     default:
       return apiUtil.sendApiError_InvalidPostData(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
@@ -74,10 +74,18 @@ settingsController.general = function (req, res) {
   const content = initViewContent('general', req)
 
   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('/')
 
   const content = initViewContent('appearance', req)
 
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
@@ -15,10 +15,11 @@
 const async = require('async')
 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 = {}
 viewdata.users = {}
 
@@ -354,19 +355,27 @@ viewController.getData = function (request, cb) {
 
           return callback()
         })
       },
       function (callback) {
-        const settingsUtil = require('../../settings/settingsUtil')
         settingsUtil.getSettings(function (err, res) {
           if (err) return callback(err)
 
           viewdata.hasThirdParty = res.data.settings.hasThirdParty
 
           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)
 
           viewdata.plugins = data
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
@@ -46,20 +46,27 @@ departmentSchema.statics.getDepartmentsByTeam = function (teamIds, callback) {
   return this.model(COLLECTION)
     .find({ teams: { $in: teamIds } })
     .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)
+      }
+    })()
   })
 }
 
 departmentSchema.statics.getDepartmentGroupsOfUser = function (userId, callback) {
   var self = this
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
@@ -163,18 +163,37 @@ groupSchema.statics.getAllPublicGroups = function (callback) {
     .sort('name')
 
   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) {
   if (_.isUndefined(userId)) return callback('Invalid UserId - GroupSchema.GetAllGroupsOfUser()')
 
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
@@ -43,21 +43,26 @@ teamSchema.pre('save', function (next) {
   this.name = utils.sanitizeFieldPlainText(this.name.trim())
 
   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) {
   if (_.isUndefined(memberId)) return callback('Invalid MemberId - TeamSchema.RemoveMember()')
 
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
@@ -89,12 +89,17 @@ define([
               text: 'Profile Successfully Saved',
               textColor: '#f8f8f2'
             })
           })
           .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)
+            }
           })
       }
 
       $scope.showTour = function () {
         var username = SessionService.getUser().username
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
@@ -2059,10 +2059,17 @@
       }
       //   errorMessage : '', // set above in switch statement
       //   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) {
         var password,
           passwordInputName = $el.valAttr('confirm') || $el.attr('name') + '_confirmation',
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
@@ -281,10 +281,11 @@ function mainRoutes (router, middleware, controllers) {
   router.get('/notices/create', middleware.redirectToLogin, middleware.loadCommonData, controllers.notices.create)
   router.get('/notices/:id', middleware.redirectToLogin, middleware.loadCommonData, controllers.notices.edit)
 
   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,
     middleware.loadCommonData,
     controllers.settings.appearance
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
@@ -120,10 +120,12 @@ util.getSettings = async callback => {
         s.privacyPolicy = parseSetting(settings, 'legal:privacypolicy', '')
         s.privacyPolicy.value = jsStringEscape(s.privacyPolicy.value)
 
         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)
 
         _.each(content.data.ticketTypes, type => {
           type.priorities = _.sortBy(type.priorities, ['migrationNum', 'name'])
diff --git a/yarn.lock b/yarn.lock
index 7d6b68cba..7e10022fe 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3876,10 +3876,15 @@ charm@~0.1.1:
 check-error@^1.0.2:
   version "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"
   integrity sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg==
   dependencies:

Vulnerable code:                   data-validation={'length'}
                  data-validation-length={'min6'}
                  data-validation-error-msg={'Password must contain at least 6 characters.'}
    const AllowUserRegistration = (
      <EnableSwitch
        stateName='allowUserRegistration'
        label='Enable'
        checked={this.getSettingsValue('allowUserRegistration')}
        onChange={e => {
          this.updateSetting('allowUserRegistration', 'allowUserRegistration:enable', e.target.checked)
        }}
      />
    )

        <SettingItem
          title='Allow User Registration'
          subtitle='Allow users to create accounts on the login screen.'
          component={AllowUserRegistration}
        />
  return res.status(errorNum).json({ success: false, error: error })
const winston = require('winston')
apiUsers.create = function (req, res) {
  const Chance = require('chance')
  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
  })
  if (postData.aTitle) {
    account.title = postData.aTitle
  }
  account.save(function (err, a) {
    if (err) {
      response.success = false
      response.error = err
      winston.debug(response)
      return res.status(400).json(response)
    }
    a.populate('role', function (err, populatedAccount) {
      if (err) return res.status(500).json({ success: false, error: err })
      response.account = populatedAccount.toObject()
      delete response.account.password
      const groups = []
      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')
            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 (err) {
          if (err) return res.status(400).json({ success: false, error: err })
          response.account.groups = groups
          return res.json(response)
        }
      )
    })
  })
accountsApi.create = function (req, res) {
  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)
              return next(null, teams)
            }
          )
        })
      },
      departments: function (next) {
        Department.getUserDepartments(savedId, next)
    },
    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 }
      })
      if ((user.role.isAgent || user.role.isAdmin) && results.teams) {
        user.teams = results.teams.map(function (t) {
          return { _id: t._id, name: t.name }
        })
        user.departments = results.departments.map(function (d) {
          return { _id: d._id, name: d.name }
        })
      }
      return apiUtil.sendApiSuccess(res, { account: user })
  )
accountsApi.update = function (req, res) {
  var username = req.params.username
  var postData = req.body
  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)
              return next(null, userGroups)
          )
        })
      },
      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)
              return next(null, userTeams)
          )
        })
      },
      departments: function (next) {
        Department.getUserDepartments(postData._id, next)
    },
    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 }
      })
      if ((user.role.isAgent || user.role.isAdmin) && results.teams) {
        user.teams = results.teams.map(function (t) {
          return { _id: t._id, name: t.name }
        })
        user.departments = results.departments.map(function (d) {
          return { _id: d._id, name: d.name }
        })
      }
      if (passwordUpdated) {
        const Session = require('../../../models/session')
        await Session.destroy(user._id)
      }
      return apiUtil.sendApiSuccess(res, { user: user })
  )
        const settingsUtil = require('../../settings/settingsUtil')
departmentSchema.statics.getUserDepartments = function (userId, callback) {
  var self = this

  Teams.getTeamsOfUser(userId, function (err, teams) {
    if (err) return callback(err)
    return self
      .model(COLLECTION)
      .find({ teams: { $in: teams } })
      .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)
teamSchema.methods.addMember = function (memberId, callback) {
  if (_.isUndefined(memberId)) return callback('Invalid MemberId - TeamSchema.AddMember()')
  if (this.members === null) this.members = []
  if (isMember(this.members, memberId)) return callback(null, false)
  this.members.push(memberId)
  this.members = _.uniq(this.members)

  return callback(null, true)
            $log.log('[trudesk:profile:updateUser] - ' + e.error.message)
            helpers.UI.showSnackbar('Error ' + e.error.message, true)
Fixed code:     "check-password-strength": "2.0.5",
                  <SubmenuItem
                    text='Accounts'
                    icon='tune'
                    href='/settings/accounts'
                    active={activeSubItem === 'settings-accounts'}
                  />
                  data-validation={this.props.common.accountsPasswordComplexity ? 'length' : 'none'}
                  data-validation-length={'min8'}
                  data-validation-error-msg={'Password must contain at least 8 characters.'}
/*
 *       .                             .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 2:20 PM
 *  Copyright (c) 2014-2022. All rights reserved.
 */

import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { updateSetting, updateMultipleSettings } from 'actions/settings'

import Button from 'components/Button'
import SettingItem from 'components/Settings/SettingItem'

import helpers from 'lib/helpers'
import axios from 'axios'
import Log from '../../../logger'
import EnableSwitch from 'components/Settings/EnableSwitch'
import { observer } from 'mobx-react'
import { makeObservable, observable } from 'mobx'
import UIKit from 'uikit'

@observer
class AccountsSettingsContainer extends React.Component {
  @observable passwordComplexityEnabled = false
  @observable allowUserRegistrationEnabled = false

  constructor (props) {
    super(props)

    makeObservable(this)

    this.state = {
      restarting: false
    }

    this.restartServer = this.restartServer.bind(this)
  }

  componentDidMount () {
    // helpers.UI.inputs()
  }

  componentDidUpdate (prevProps) {
    // helpers.UI.reRenderInputs()
    if (prevProps.settings !== this.props.settings) {
      if (this.passwordComplexityEnabled !== this.getSetting('accountsPasswordComplexity'))
        this.passwordComplexityEnabled = this.getSetting('accountsPasswordComplexity')
      if (this.allowUserRegistrationEnabled !== this.getSetting('allowUserRegistration'))
        this.allowUserRegistrationEnabled = this.getSetting('allowUserRegistration')
    }
  }

  restartServer () {
    this.setState({ restarting: true })

    const token = document.querySelector('meta[name="csrf-token"]').getAttribute('content')
    axios
      .post(
        '/api/v1/admin/restart',
        {},
        {
          headers: {
            'CSRF-TOKEN': token
          }
        }
      )
      .catch(error => {
        helpers.hideLoader()
        Log.error(error.responseText)
        Log.error('Unable to restart server. Server must run under PM2 and Account must have admin rights.')
        helpers.UI.showSnackbar('Unable to restart server. Are you an Administrator?', true)
      })
      .then(() => {
        this.setState({ restarting: false })
      })
  }

  getSetting (stateName) {
    return this.props.settings.getIn(['settings', stateName, 'value'])
      ? this.props.settings.getIn(['settings', stateName, 'value'])
      : ''
  }

  updateSetting (stateName, name, value) {
    this.props.updateSetting({ stateName, name, value })
  }

  render () {
    const { active } = this.props
    return (
      <div className={active ? 'active' : 'hide'}>
        <SettingItem
          title='Allow User Registration'
          subtitle='Allow users to create accounts on the login screen.'
          component={
            <EnableSwitch
              stateName='allowUserRegistration'
              label='Enable'
              checked={this.allowUserRegistrationEnabled}
              onChange={e => {
                this.updateSetting('allowUserRegistration', 'allowUserRegistration:enable', e.target.checked)
              }}
            />
          }
        />
        <SettingItem
          title={'Password Complexity'}
          subtitle={'Require users passwords to meet minimum password complexity'}
          tooltip={'Minimum 8 characters with uppercase and numeric.'}
          component={
            <EnableSwitch
              stateName={'accountsPasswordComplexity'}
              label={'Enable'}
              checked={this.passwordComplexityEnabled}
              onChange={e => {
                this.updateSetting('accountsPasswordComplexity', 'accountsPasswordComplexity:enable', e.target.checked)
              }}
            />
          }
        />
      </div>
    )
  }
}

AccountsSettingsContainer.propTypes = {
  active: PropTypes.bool.isRequired,
  updateSetting: PropTypes.func.isRequired,
  updateMultipleSettings: PropTypes.func.isRequired,
  settings: PropTypes.object.isRequired
}

const mapStateToProps = state => ({
  settings: state.settings.settings
})

export default connect(mapStateToProps, { updateSetting, updateMultipleSettings })(AccountsSettingsContainer)
import AccountsSettings from './Accounts'
              <MenuItem
                title='Accounts'
                active={this.state.activeCategory === 'settings-accounts'}
                onClick={e => {
                  this.onMenuItemClick(e, 'accounts')
                }}
              />
              <AccountsSettings active={this.state.activeCategory === 'settings-accounts'} />
    if (errorText.message) errorText = errorText.message
  return res.status(errorNum).json({ success: false, error })
const winston = require('../../../logger')
const SettingUtil = require('../../../settings/settingsUtil')
const Chance = require('chance')
apiUsers.create = async function (req, res) {
  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.' })
            return next()
          }
          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
        })
        if (postData.aTitle) {
          account.title = postData.aTitle
        }
        account.save(function (err, a) {
          if (err) return next(err)
          a.populate('role', function (err, populatedAccount) {
            if (err) return next(err)
            response.account = populatedAccount.toObject()
            delete response.account.password
            const groups = []
            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
                return next()
              }
            )
        })
      }
    ],
    function (e) {
      if (e) {
        response.success = false
        response.error = e
        winston.debug(response)
        return res.status(400).json(response)
      }

      return res.json(response)
    }
  )
      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)
        })
      },
  let passwordComplexityEnabled = true

      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()
        })
      },
              if (passwordComplexityEnabled) {
                // check Password Complexity
                const passwordComplexity = require('../../../settings/passwordComplexity')
                if (!passwordComplexity.validate(obj.password)) return done('Password does not meet requirements')
              }

const winston = require('../../../logger')
const passwordComplexity = require('../../../settings/passwordComplexity')
accountsApi.create = async function (req, res) {
  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()
      }
    }
    let teams = []
    if (postData.teams) {
      const dbTeams = await Team.getTeamsByIds(postData.teams)
      for (const team of dbTeams) {
        await team.addMember(savedId)
        await team.save()
      teams = dbTeams
    }
    const departments = await Department.getUserDepartments(savedId)
    user = userPopulated.toJSON()
    user.groups = groups.map(g => {
      return { _id: g._id, name: g.name }
    })
    if ((user.role.isAgent || user.role.isAdmin) && teams.length > 0) {
      user.teams = teams.map(t => {
        return { _id: t._id, name: t.name }
      })
      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.update = async function (req, res) {
  const username = req.params.username
  const postData = req.body
  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.')
    }
    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)
          }
        } else {
          const result = await g.removeMember(postData._id)
          if (result) await g.save()
        }
      }
    }
    // 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)
          }
        } else {
          const result = await t.removeMember(postData._id)
          if (result) await t.save()
        }
    }
    // DEPARTMENTS
    const departments = await Department.getUserDepartments(postData._id)
    user = resUser.toJSON()
    user.groups = groups.map(g => {
      return { _id: g._id, name: g.name }
    })
    if ((user.role.isAgent || user.role.isAdmin) && teams.length > 0) {
      user.teams = teams.map(t => {
        return { _id: t._id, name: t.name }
      })
      user.departments = departments.map(d => {
        return { _id: d._id, name: d.name }
      })
    }
    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)
  }
settingsController.accounts = function (req, res) {
  if (!checkPerms(req, 'settings:view')) return res.redirect('/')

  const content = initViewContent('accounts', req)

  renderView(res, content)
}

const settingsUtil = require('../../settings/settingsUtil')
      function (callback) {
        settingsUtil.getSettings(function (err, res) {
          if (err) return callback(err)

          viewdata.accountsPasswordComplexity = res.data.settings.accountsPasswordComplexity.value

          return callback()
        })
      },
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)
        }
        const departments = await exec.exec()
        return resolve(departments)
      } catch (e) {
        return reject(e)
      }
    })()
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)
      }
    })()
  })
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 = []
      this.members.push(memberId)
      this.members = _.uniq(this.members)
      return resolve(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)
            }
    $.formUtils.addValidator({
      name: 'none',
      validatorFunction: function(value, $el, config, language, $form) {
        return true
      }
    })

  router.get('/settings/accounts', middleware.redirectToLogin, middleware.loadCommonData, controllers.settings.accounts)
/*
 *       .                             .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
        s.accountsPasswordComplexity = parseSetting(settings, 'accountsPasswordComplexity:enable', true)

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==