Skip to content

Commit

Permalink
401-Password Complexity Check Capability (#402)
Browse files Browse the repository at this point in the history
* Added improved password complexity checking capability.

* Move password complexity checker as User.util
dynamically import required libraries depending on code execution flow
lint

* Ensure persistence of password requirements on restarts via env-dump
Copy example schema to docker env as well

---------

Co-authored-by: timothycarambat <[email protected]>
  • Loading branch information
pritchey and timothycarambat authored Dec 5, 2023
1 parent 7b30dd0 commit 732d078
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 6 deletions.
14 changes: 14 additions & 0 deletions docker/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,17 @@ VECTOR_DB="lancedb"
STORAGE_DIR="/app/server/storage"
UID='1000'
GID='1000'

###########################################
######## PASSWORD COMPLEXITY ##############
###########################################
# Enforce a password schema for your organization users.
# Documentation on how to use https://github.com/kamronbatman/joi-password-complexity
# Default is only 8 char minimum
# PASSWORDMINCHAR=8
# PASSWORDMAXCHAR=250
# PASSWORDLOWERCASE=1
# PASSWORDUPPERCASE=1
# PASSWORDNUMERIC=1
# PASSWORDSYMBOL=1
# PASSWORDREQUIREMENTS=4
1 change: 0 additions & 1 deletion frontend/src/pages/Admin/Users/NewUserModal/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ export default function NewUserModal() {
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
placeholder="User's initial password"
required={true}
minLength={8}
autoComplete="off"
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ export default function EditUserModal({ currentUser, user }) {
type="text"
className="bg-zinc-900 border border-gray-500 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
placeholder={`${user.username}'s new password`}
minLength={8}
autoComplete="off"
/>
</div>
Expand Down
13 changes: 13 additions & 0 deletions server/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,16 @@ VECTOR_DB="lancedb"
# AUTH_TOKEN="hunter2" # This is the password to your application if remote hosting.
# STORAGE_DIR= # absolute filesystem path with no trailing slash
# NO_DEBUG="true"

###########################################
######## PASSWORD COMPLEXITY ##############
###########################################
# Enforce a password schema for your organization users.
# Documentation on how to use https://github.com/kamronbatman/joi-password-complexity
#PASSWORDMINCHAR=8
#PASSWORDMAXCHAR=250
#PASSWORDLOWERCASE=1
#PASSWORDUPPERCASE=1
#PASSWORDNUMERIC=1
#PASSWORDSYMBOL=1
#PASSWORDREQUIREMENTS=4
50 changes: 46 additions & 4 deletions server/models/user.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
const prisma = require("../utils/prisma");
const bcrypt = require("bcrypt");

const User = {
create: async function ({ username, password, role = "default" }) {
const passwordCheck = this.checkPasswordComplexity(password);
if (!passwordCheck.checkedOK) {
return { user: null, error: passwordCheck.error };
}

try {
const bcrypt = require("bcrypt");
const hashedPassword = bcrypt.hashSync(password, 10);
const user = await prisma.users.create({
data: {
Expand All @@ -21,9 +26,14 @@ const User = {

update: async function (userId, updates = {}) {
try {
// Rehash new password if it exists as update
// will be given to us as plaintext.
if (updates.hasOwnProperty("password") && updates.password.length >= 8) {
// Rehash new password if it exists as update field
if (updates.hasOwnProperty("password")) {
const passwordCheck = this.checkPasswordComplexity(updates.password);
if (!passwordCheck.checkedOK) {
return { success: false, error: passwordCheck.error };
}

const bcrypt = require("bcrypt");
updates.password = bcrypt.hashSync(updates.password, 10);
} else {
delete updates.password;
Expand Down Expand Up @@ -82,6 +92,38 @@ const User = {
return [];
}
},

checkPasswordComplexity: function (passwordInput = "") {
const passwordComplexity = require("joi-password-complexity");
// Can be set via ENV variable on boot. No frontend config at this time.
// Docs: https://www.npmjs.com/package/joi-password-complexity
const complexityOptions = {
min: process.env.PASSWORDMINCHAR || 8,
max: process.env.PASSWORDMAXCHAR || 250,
lowerCase: process.env.PASSWORDLOWERCASE || 0,
upperCase: process.env.PASSWORDUPPERCASE || 0,
numeric: process.env.PASSWORDNUMERIC || 0,
symbol: process.env.PASSWORDSYMBOL || 0,
// reqCount should be equal to how many conditions you are testing for (1-4)
requirementCount: process.env.PASSWORDREQUIREMENTS || 0,
};

const complexityCheck = passwordComplexity(
complexityOptions,
"password"
).validate(passwordInput);
if (complexityCheck.hasOwnProperty("error")) {
let myError = "";
let prepend = "";
for (let i = 0; i < complexityCheck.error.details.length; i++) {
myError += prepend + complexityCheck.error.details[i].message;
prepend = ", ";
}
return { checkedOK: false, error: myError };
}

return { checkedOK: true, error: "No error." };
},
};

module.exports = { User };
2 changes: 2 additions & 0 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
"express": "^4.18.2",
"extract-zip": "^2.0.1",
"graphql": "^16.7.1",
"joi": "^17.11.0",
"joi-password-complexity": "^5.2.0",
"js-tiktoken": "^1.0.7",
"jsonwebtoken": "^8.5.1",
"langchain": "^0.0.90",
Expand Down
8 changes: 8 additions & 0 deletions server/utils/helpers/updateENV.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,14 @@ async function dumpENV() {
"CACHE_VECTORS",
"STORAGE_DIR",
"SERVER_PORT",
// Password Schema Keys if present.
"PASSWORDMINCHAR",
"PASSWORDMAXCHAR",
"PASSWORDLOWERCASE",
"PASSWORDUPPERCASE",
"PASSWORDNUMERIC",
"PASSWORDSYMBOL",
"PASSWORDREQUIREMENTS",
];

for (const key of protectedKeys) {
Expand Down
45 changes: 45 additions & 0 deletions server/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,18 @@
resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861"
integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==

"@hapi/hoek@^9.0.0":
version "9.3.0"
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb"
integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==

"@hapi/topo@^5.0.0":
version "5.1.0"
resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012"
integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==
dependencies:
"@hapi/hoek" "^9.0.0"

"@mapbox/node-pre-gyp@^1.0.0", "@mapbox/node-pre-gyp@^1.0.10":
version "1.0.11"
resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa"
Expand Down Expand Up @@ -224,6 +236,23 @@
resolved "https://registry.yarnpkg.com/@sevinf/maybe/-/maybe-0.5.0.tgz#e59fcea028df615fe87d708bb30e1f338e46bb44"
integrity sha512-ARhyoYDnY1LES3vYI0fiG6e9esWfTNcXcO6+MPJJXcnyMV3bim4lnFt45VXouV7y82F4x3YH8nOQ6VztuvUiWg==

"@sideway/address@^4.1.3":
version "4.1.4"
resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0"
integrity sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==
dependencies:
"@hapi/hoek" "^9.0.0"

"@sideway/formula@^3.0.1":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f"
integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==

"@sideway/pinpoint@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df"
integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==

"@tootallnate/once@1":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
Expand Down Expand Up @@ -1556,6 +1585,22 @@ isomorphic-fetch@^3.0.0:
node-fetch "^2.6.1"
whatwg-fetch "^3.4.1"

joi-password-complexity@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/joi-password-complexity/-/joi-password-complexity-5.2.0.tgz#5308f4e7c6c39ce0a6a050597883d5fd7f2800b4"
integrity sha512-exQOcaKC4EuZwwNVQ/5/FcnCzdwdzjA2RPIrRgZXTjzkFhY5NUtP83SlcNSUK3OvbRFpjUq1FCzhHg/uqPg90g==

joi@^17.11.0:
version "17.11.0"
resolved "https://registry.yarnpkg.com/joi/-/joi-17.11.0.tgz#aa9da753578ec7720e6f0ca2c7046996ed04fc1a"
integrity sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==
dependencies:
"@hapi/hoek" "^9.0.0"
"@hapi/topo" "^5.0.0"
"@sideway/address" "^4.1.3"
"@sideway/formula" "^3.0.1"
"@sideway/pinpoint" "^2.0.0"

js-tiktoken@^1.0.6, js-tiktoken@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/js-tiktoken/-/js-tiktoken-1.0.7.tgz#56933fcd2093e8304060dfde3071bda91812e6f5"
Expand Down

0 comments on commit 732d078

Please sign in to comment.