Skip to content
Snippets Groups Projects
Unverified Commit 732d0782 authored by pritchey's avatar pritchey Committed by GitHub
Browse files

401-Password Complexity Check Capability (#402)


* 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: default avatartimothycarambat <rambat1010@gmail.com>
parent 7b30dd04
No related branches found
No related tags found
No related merge requests found
...@@ -79,3 +79,17 @@ VECTOR_DB="lancedb" ...@@ -79,3 +79,17 @@ VECTOR_DB="lancedb"
STORAGE_DIR="/app/server/storage" STORAGE_DIR="/app/server/storage"
UID='1000' UID='1000'
GID='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
\ No newline at end of file
...@@ -77,7 +77,6 @@ export default function NewUserModal() { ...@@ -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" 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" placeholder="User's initial password"
required={true} required={true}
minLength={8}
autoComplete="off" autoComplete="off"
/> />
</div> </div>
......
...@@ -77,7 +77,6 @@ export default function EditUserModal({ currentUser, user }) { ...@@ -77,7 +77,6 @@ export default function EditUserModal({ currentUser, user }) {
type="text" 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" 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`} placeholder={`${user.username}'s new password`}
minLength={8}
autoComplete="off" autoComplete="off"
/> />
</div> </div>
......
...@@ -78,3 +78,16 @@ VECTOR_DB="lancedb" ...@@ -78,3 +78,16 @@ VECTOR_DB="lancedb"
# AUTH_TOKEN="hunter2" # This is the password to your application if remote hosting. # AUTH_TOKEN="hunter2" # This is the password to your application if remote hosting.
# STORAGE_DIR= # absolute filesystem path with no trailing slash # STORAGE_DIR= # absolute filesystem path with no trailing slash
# NO_DEBUG="true" # 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
const prisma = require("../utils/prisma"); const prisma = require("../utils/prisma");
const bcrypt = require("bcrypt");
const User = { const User = {
create: async function ({ username, password, role = "default" }) { create: async function ({ username, password, role = "default" }) {
const passwordCheck = this.checkPasswordComplexity(password);
if (!passwordCheck.checkedOK) {
return { user: null, error: passwordCheck.error };
}
try { try {
const bcrypt = require("bcrypt");
const hashedPassword = bcrypt.hashSync(password, 10); const hashedPassword = bcrypt.hashSync(password, 10);
const user = await prisma.users.create({ const user = await prisma.users.create({
data: { data: {
...@@ -21,9 +26,14 @@ const User = { ...@@ -21,9 +26,14 @@ const User = {
update: async function (userId, updates = {}) { update: async function (userId, updates = {}) {
try { try {
// Rehash new password if it exists as update // Rehash new password if it exists as update field
// will be given to us as plaintext. if (updates.hasOwnProperty("password")) {
if (updates.hasOwnProperty("password") && updates.password.length >= 8) { 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); updates.password = bcrypt.hashSync(updates.password, 10);
} else { } else {
delete updates.password; delete updates.password;
...@@ -82,6 +92,38 @@ const User = { ...@@ -82,6 +92,38 @@ const User = {
return []; 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 }; module.exports = { User };
...@@ -36,6 +36,8 @@ ...@@ -36,6 +36,8 @@
"express": "^4.18.2", "express": "^4.18.2",
"extract-zip": "^2.0.1", "extract-zip": "^2.0.1",
"graphql": "^16.7.1", "graphql": "^16.7.1",
"joi": "^17.11.0",
"joi-password-complexity": "^5.2.0",
"js-tiktoken": "^1.0.7", "js-tiktoken": "^1.0.7",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"langchain": "^0.0.90", "langchain": "^0.0.90",
......
...@@ -286,6 +286,14 @@ async function dumpENV() { ...@@ -286,6 +286,14 @@ async function dumpENV() {
"CACHE_VECTORS", "CACHE_VECTORS",
"STORAGE_DIR", "STORAGE_DIR",
"SERVER_PORT", "SERVER_PORT",
// Password Schema Keys if present.
"PASSWORDMINCHAR",
"PASSWORDMAXCHAR",
"PASSWORDLOWERCASE",
"PASSWORDUPPERCASE",
"PASSWORDNUMERIC",
"PASSWORDSYMBOL",
"PASSWORDREQUIREMENTS",
]; ];
for (const key of protectedKeys) { for (const key of protectedKeys) {
......
...@@ -150,6 +150,18 @@ ...@@ -150,6 +150,18 @@
resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861"
integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== 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": "@mapbox/node-pre-gyp@^1.0.0", "@mapbox/node-pre-gyp@^1.0.10":
version "1.0.11" version "1.0.11"
resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa" resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa"
...@@ -224,6 +236,23 @@ ...@@ -224,6 +236,23 @@
resolved "https://registry.yarnpkg.com/@sevinf/maybe/-/maybe-0.5.0.tgz#e59fcea028df615fe87d708bb30e1f338e46bb44" resolved "https://registry.yarnpkg.com/@sevinf/maybe/-/maybe-0.5.0.tgz#e59fcea028df615fe87d708bb30e1f338e46bb44"
integrity sha512-ARhyoYDnY1LES3vYI0fiG6e9esWfTNcXcO6+MPJJXcnyMV3bim4lnFt45VXouV7y82F4x3YH8nOQ6VztuvUiWg== 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": "@tootallnate/once@1":
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
...@@ -1556,6 +1585,22 @@ isomorphic-fetch@^3.0.0: ...@@ -1556,6 +1585,22 @@ isomorphic-fetch@^3.0.0:
node-fetch "^2.6.1" node-fetch "^2.6.1"
whatwg-fetch "^3.4.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: js-tiktoken@^1.0.6, js-tiktoken@^1.0.7:
version "1.0.7" version "1.0.7"
resolved "https://registry.yarnpkg.com/js-tiktoken/-/js-tiktoken-1.0.7.tgz#56933fcd2093e8304060dfde3071bda91812e6f5" resolved "https://registry.yarnpkg.com/js-tiktoken/-/js-tiktoken-1.0.7.tgz#56933fcd2093e8304060dfde3071bda91812e6f5"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment