now typescript
This commit is contained in:
Vendored
+16
@@ -0,0 +1,16 @@
|
|||||||
|
"use strict";
|
||||||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.PasswordGenerator = exports.RateLimit = exports.PassPolicy = exports.JwtAuth = exports.PassCheck = void 0;
|
||||||
|
const passwordcheck_1 = __importDefault(require("./passwordcheck"));
|
||||||
|
exports.PassCheck = passwordcheck_1.default;
|
||||||
|
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
||||||
|
exports.JwtAuth = jsonwebtoken_1.default;
|
||||||
|
const passpolicy_1 = __importDefault(require("./passpolicy"));
|
||||||
|
exports.PassPolicy = passpolicy_1.default;
|
||||||
|
const ratelimit_1 = __importDefault(require("./ratelimit"));
|
||||||
|
exports.RateLimit = ratelimit_1.default;
|
||||||
|
const passgen_1 = __importDefault(require("./passgen"));
|
||||||
|
exports.PasswordGenerator = passgen_1.default;
|
||||||
Vendored
+121
@@ -0,0 +1,121 @@
|
|||||||
|
"use strict";
|
||||||
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||||
|
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||||
|
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||||
|
}
|
||||||
|
Object.defineProperty(o, k2, desc);
|
||||||
|
}) : (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
o[k2] = m[k];
|
||||||
|
}));
|
||||||
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||||
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||||
|
}) : function(o, v) {
|
||||||
|
o["default"] = v;
|
||||||
|
});
|
||||||
|
var __importStar = (this && this.__importStar) || function (mod) {
|
||||||
|
if (mod && mod.__esModule) return mod;
|
||||||
|
var result = {};
|
||||||
|
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||||
|
__setModuleDefault(result, mod);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
const jwt = __importStar(require("jsonwebtoken"));
|
||||||
|
class JwtAuth {
|
||||||
|
constructor(JWTSecretKey) {
|
||||||
|
this.JWTSecretKey = JWTSecretKey;
|
||||||
|
this.blacklist = [];
|
||||||
|
}
|
||||||
|
generateJWT(payload, settings = {}, secretKey = this.JWTSecretKey) {
|
||||||
|
return jwt.sign(payload, secretKey, settings);
|
||||||
|
}
|
||||||
|
verifyJWT(token, secretKey = this.JWTSecretKey) {
|
||||||
|
try {
|
||||||
|
if (this.blacklist.includes(token))
|
||||||
|
return { valid: false, message: "Token is blacklisted." };
|
||||||
|
const vertoken = jwt.verify(token, secretKey);
|
||||||
|
if (vertoken instanceof Object) {
|
||||||
|
return vertoken;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
return { valid: false, message: "Token is invalid." };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
decodeJWT(token) {
|
||||||
|
if (this.blacklist.includes(token))
|
||||||
|
return { valid: false, message: "Token is blacklisted." };
|
||||||
|
return jwt.decode(token);
|
||||||
|
}
|
||||||
|
getJWTExpirationDate(token) {
|
||||||
|
if (this.blacklist.includes(token))
|
||||||
|
return { valid: false, message: "Token is blacklisted." };
|
||||||
|
const decoded = this.decodeJWT(token);
|
||||||
|
return decoded.exp;
|
||||||
|
}
|
||||||
|
isJWTExpired(token) {
|
||||||
|
if (this.blacklist.includes(token))
|
||||||
|
return { valid: false, message: "Token is blacklisted." };
|
||||||
|
const expirationDate = this.getJWTExpirationDate(token);
|
||||||
|
return expirationDate < Date.now();
|
||||||
|
}
|
||||||
|
refreshJWT(token, settings = {}, secretKey = this.JWTSecretKey) {
|
||||||
|
if (this.blacklist.includes(token))
|
||||||
|
return { valid: false, message: "Token is blacklisted." };
|
||||||
|
const decoded = this.verifyJWT(token, secretKey);
|
||||||
|
if (decoded instanceof Object) {
|
||||||
|
if (decoded.valid == false)
|
||||||
|
return { valid: false, message: "Token is invalid." };
|
||||||
|
let payLoadArray = {};
|
||||||
|
if (decoded instanceof Object) {
|
||||||
|
payLoadArray = decoded;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return { valid: false, message: "Token is invalid." };
|
||||||
|
}
|
||||||
|
const newToken = this.generateJWT(payLoadArray, settings, secretKey);
|
||||||
|
return newToken;
|
||||||
|
}
|
||||||
|
return { valid: false, message: "Token is invalid." };
|
||||||
|
}
|
||||||
|
BlackListJWT(token) {
|
||||||
|
//check if token is already blacklisted
|
||||||
|
if (this.blacklist.includes(token)) {
|
||||||
|
return { valid: false, message: "Token is already blacklisted." };
|
||||||
|
}
|
||||||
|
//add token to blacklist
|
||||||
|
this.blacklist.push(token);
|
||||||
|
return { valid: true, message: "Token successfully blacklisted." };
|
||||||
|
}
|
||||||
|
ClearBlackList() {
|
||||||
|
//clear blacklist
|
||||||
|
this.blacklist = [];
|
||||||
|
return { valid: true, message: "Blacklist successfully cleared." };
|
||||||
|
}
|
||||||
|
GetBlackList() {
|
||||||
|
//return blacklist
|
||||||
|
return this.blacklist;
|
||||||
|
}
|
||||||
|
RemoveFromBlackList(token) {
|
||||||
|
//remove token from blacklist
|
||||||
|
if (this.blacklist.includes(token)) {
|
||||||
|
this.blacklist = this.blacklist.filter((item) => item !== token);
|
||||||
|
return { valid: true, message: "Token successfully removed from blacklist." };
|
||||||
|
}
|
||||||
|
return { valid: false, message: "Token is not blacklisted." };
|
||||||
|
}
|
||||||
|
IsBlackListed(token) {
|
||||||
|
//check if token is blacklisted
|
||||||
|
if (this.blacklist.includes(token)) {
|
||||||
|
return { valid: true, message: "Token is blacklisted." };
|
||||||
|
}
|
||||||
|
return { valid: false, message: "Token is not blacklisted." };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.default = JwtAuth;
|
||||||
Vendored
+51
@@ -0,0 +1,51 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
class PasswordGenerator {
|
||||||
|
constructor(options) {
|
||||||
|
const defaultOptions = {
|
||||||
|
minLength: 6,
|
||||||
|
maxLength: 32,
|
||||||
|
minLower: 2,
|
||||||
|
minUpper: 2,
|
||||||
|
minNum: 2,
|
||||||
|
minSpecial: 3,
|
||||||
|
specialChars: "!@#$%^&*()_+~`|}{[]:;?><,./-=",
|
||||||
|
};
|
||||||
|
this.options = Object.assign(Object.assign({}, defaultOptions), options);
|
||||||
|
}
|
||||||
|
Generate(length = 0) {
|
||||||
|
// Generate random password that complies with the options
|
||||||
|
const { minLength, maxLength, minLower, minUpper, minNum, minSpecial, specialChars } = this.options;
|
||||||
|
let pass = "";
|
||||||
|
let lowerRegex = 'abcdefghijklmnopqrstuvwxyz';
|
||||||
|
let upperRegex = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||||
|
let numRegex = '0123456789';
|
||||||
|
// Generate password
|
||||||
|
//generate random length
|
||||||
|
if (length === undefined || length < minLength || length > maxLength || length === 0) {
|
||||||
|
length = Math.floor(Math.random() * (maxLength - minLength + 1) + minLength);
|
||||||
|
}
|
||||||
|
//generate random length for each character type
|
||||||
|
let lowerLength = Math.floor(Math.random() * (length - minLower - minUpper - minNum - minSpecial + 1) + minLower);
|
||||||
|
let upperLength = Math.floor(Math.random() * (length - lowerLength - minUpper - minNum - minSpecial + 1) + minUpper);
|
||||||
|
let numLength = Math.floor(Math.random() * (length - lowerLength - upperLength - minNum - minSpecial + 1) + minNum);
|
||||||
|
let specialLength = Math.floor(Math.random() * (length - lowerLength - upperLength - numLength - minSpecial + 1) + minSpecial);
|
||||||
|
//generate random characters
|
||||||
|
for (let i = 0; i < lowerLength; i++) {
|
||||||
|
pass += lowerRegex[Math.floor(Math.random() * lowerRegex.length)];
|
||||||
|
}
|
||||||
|
for (let i = 0; i < upperLength; i++) {
|
||||||
|
pass += upperRegex[Math.floor(Math.random() * upperRegex.length)];
|
||||||
|
}
|
||||||
|
for (let i = 0; i < numLength; i++) {
|
||||||
|
pass += numRegex[Math.floor(Math.random() * numRegex.length)];
|
||||||
|
}
|
||||||
|
for (let i = 0; i < specialLength; i++) {
|
||||||
|
pass += specialChars[Math.floor(Math.random() * specialChars.length)];
|
||||||
|
}
|
||||||
|
//shuffle password
|
||||||
|
pass = pass.split('').sort(function () { return 0.5 - Math.random(); }).join('');
|
||||||
|
return pass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.default = PasswordGenerator;
|
||||||
Vendored
+25
-23
@@ -1,35 +1,31 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
class PassPolicy {
|
class PassPolicy {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
// Define default options and merge with provided options
|
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
minLength: 6,
|
minLength: 6,
|
||||||
maxLength: 256,
|
maxLength: 32,
|
||||||
minLower: 1,
|
minLower: 2,
|
||||||
minUpper: 1,
|
minUpper: 2,
|
||||||
minNum: 1,
|
minNum: 2,
|
||||||
minSpecial: 0,
|
minSpecial: 3,
|
||||||
specialChars: "!@#$%^&*()_+~`|}{[]:;?><,./-=",
|
specialChars: "!@#$%^&*()_+~`|}{[]:;?><,./-=",
|
||||||
};
|
};
|
||||||
this.options = { ...defaultOptions, ...options };
|
this.options = Object.assign(Object.assign({}, defaultOptions), options);
|
||||||
}
|
}
|
||||||
|
|
||||||
validate(password) {
|
validate(password) {
|
||||||
const { minLength, maxLength, minLower, minUpper, minNum, minSpecial, specialChars } = this.options;
|
const { minLength, maxLength, minLower, minUpper, minNum, minSpecial, specialChars } = this.options;
|
||||||
|
|
||||||
if (password.length < minLength || password.length > maxLength) {
|
if (password.length < minLength || password.length > maxLength) {
|
||||||
return { valid: false, message: "Password length does not meet requirements." };
|
return { valid: false, message: "Password length does not meet requirements." };
|
||||||
}
|
}
|
||||||
|
|
||||||
const lowerRegex = /[a-z]/g;
|
const lowerRegex = /[a-z]/g;
|
||||||
const upperRegex = /[A-Z]/g;
|
const upperRegex = /[A-Z]/g;
|
||||||
const numRegex = /[0-9]/g;
|
const numRegex = /[0-9]/g;
|
||||||
const specialRegex = new RegExp(`[${specialChars.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&")}]`, "g");
|
const specialRegex = new RegExp(`[${specialChars.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&")}]`, "g");
|
||||||
|
|
||||||
const lowerCount = (password.match(lowerRegex) || []).length;
|
const lowerCount = (password.match(lowerRegex) || []).length;
|
||||||
const upperCount = (password.match(upperRegex) || []).length;
|
const upperCount = (password.match(upperRegex) || []).length;
|
||||||
const numCount = (password.match(numRegex) || []).length;
|
const numCount = (password.match(numRegex) || []).length;
|
||||||
const specialCount = (password.match(specialRegex) || []).length;
|
const specialCount = (password.match(specialRegex) || []).length;
|
||||||
|
|
||||||
if (lowerCount < minLower) {
|
if (lowerCount < minLower) {
|
||||||
return { valid: false, message: "Password must contain at least " + minLower + " lowercase letter(s)." };
|
return { valid: false, message: "Password must contain at least " + minLower + " lowercase letter(s)." };
|
||||||
}
|
}
|
||||||
@@ -42,31 +38,37 @@ class PassPolicy {
|
|||||||
if (specialCount < minSpecial) {
|
if (specialCount < minSpecial) {
|
||||||
return { valid: false, message: "Password must contain at least " + minSpecial + " special character(s)." };
|
return { valid: false, message: "Password must contain at least " + minSpecial + " special character(s)." };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { valid: true };
|
return { valid: true };
|
||||||
}
|
}
|
||||||
|
CheckDifference(newPassword, oldPassword, neededDifference = 3) {
|
||||||
CheckDifference(newPassword, oldPassword, neededDifference) {
|
|
||||||
//check if new password is different from old password
|
//check if new password is different from old password
|
||||||
if (newPassword === oldPassword) {
|
if (newPassword === oldPassword) {
|
||||||
return { valid: false, message: "New password must be different from old password." };
|
return { valid: false, message: "New password must be different from old password." };
|
||||||
}
|
}
|
||||||
|
|
||||||
//check how many characters are different
|
//check how many characters are different
|
||||||
let diffCount = 0;
|
let diffCount = 0;
|
||||||
for (let i = 0; i < newPassword.length; i++) {
|
//check if new password is longer than old password
|
||||||
if (newPassword[i] !== oldPassword[i]) {
|
if (newPassword.length > oldPassword.length) {
|
||||||
diffCount++;
|
for (let i = 0; i < oldPassword.length; i++) {
|
||||||
|
if (newPassword[i] != oldPassword[i]) {
|
||||||
|
diffCount++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
diffCount += newPassword.length - oldPassword.length;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (let i = 0; i < newPassword.length; i++) {
|
||||||
|
if (newPassword[i] != oldPassword[i]) {
|
||||||
|
diffCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
diffCount += oldPassword.length - newPassword.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
//check if difference is enough
|
//check if difference is enough
|
||||||
if (diffCount < neededDifference) {
|
if (diffCount < neededDifference) {
|
||||||
return { valid: false, message: "New password must be different from old password by at least " + neededDifference + " characters." };
|
return { valid: false, message: "New password must be different from old password by at least " + neededDifference + " characters." };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { valid: true };
|
return { valid: true };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
exports.default = PassPolicy;
|
||||||
module.exports = PassPolicy;
|
|
||||||
Vendored
+35
@@ -0,0 +1,35 @@
|
|||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
const bcrypt_1 = __importDefault(require("bcrypt"));
|
||||||
|
const passpolicy_1 = __importDefault(require("./passpolicy"));
|
||||||
|
class PassCheck {
|
||||||
|
constructor(BcryptSaltRounds, PassPolicyOptions) {
|
||||||
|
this.BcryptSaltRounds = BcryptSaltRounds;
|
||||||
|
this.PassPolicy = new passpolicy_1.default(PassPolicyOptions);
|
||||||
|
}
|
||||||
|
verifyPassword(password, hash) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
return yield bcrypt_1.default.compare(password, hash);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
hashPassword(password) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const salt = yield bcrypt_1.default.genSalt(this.BcryptSaltRounds);
|
||||||
|
const hash = yield bcrypt_1.default.hash(password, salt);
|
||||||
|
return hash;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.default = PassCheck;
|
||||||
Vendored
+166
@@ -0,0 +1,166 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
class RateLimit {
|
||||||
|
constructor(users = {}, events = {}) {
|
||||||
|
this.users = users;
|
||||||
|
this.events = events;
|
||||||
|
this.users = users;
|
||||||
|
this.events = events;
|
||||||
|
}
|
||||||
|
addEvent(event) {
|
||||||
|
try {
|
||||||
|
this.events[event.name] = event;
|
||||||
|
//add event to all users
|
||||||
|
for (const token in this.users) {
|
||||||
|
this.users[token].events[event.name] = event;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removeEvent(name) {
|
||||||
|
try {
|
||||||
|
delete this.events[name];
|
||||||
|
//remove event from all users
|
||||||
|
for (const token in this.users) {
|
||||||
|
delete this.users[token].events[name];
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addUser(token) {
|
||||||
|
try {
|
||||||
|
this.users[token] = { token, events: this.events };
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removeUser(token) {
|
||||||
|
try {
|
||||||
|
delete this.users[token];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attempt(token, name) {
|
||||||
|
try {
|
||||||
|
if (!this.users[token] || !this.users[token].events[name]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const event = this.users[token].events[name];
|
||||||
|
if (event.attempts === undefined) {
|
||||||
|
event.attempts = [];
|
||||||
|
}
|
||||||
|
const now = Date.now();
|
||||||
|
if (event.lastAttempt && now - event.lastAttempt < event.cooldown) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
event.lastAttempt = now;
|
||||||
|
event.attempts.push(now);
|
||||||
|
if (event.attempts.length > event.maxAttempts) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getEvents() {
|
||||||
|
return this.events;
|
||||||
|
}
|
||||||
|
getUsers() {
|
||||||
|
return this.users;
|
||||||
|
}
|
||||||
|
getEvent(name) {
|
||||||
|
return this.events[name];
|
||||||
|
}
|
||||||
|
getUser(token) {
|
||||||
|
return this.users[token];
|
||||||
|
}
|
||||||
|
remainingAttempts(token, name) {
|
||||||
|
try {
|
||||||
|
if (!this.users[token] || !this.users[token].events[name]) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
const event = this.users[token].events[name];
|
||||||
|
if (event.attempts === undefined) {
|
||||||
|
event.attempts = [];
|
||||||
|
}
|
||||||
|
return event.maxAttempts - event.attempts.length;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resetAttempts(token, name) {
|
||||||
|
try {
|
||||||
|
if (!this.users[token] || !this.users[token].events[name]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const event = this.users[token].events[name];
|
||||||
|
event.attempts = [];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resetAllAttempts(token) {
|
||||||
|
try {
|
||||||
|
if (!this.users[token]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (const name in this.users[token].events) {
|
||||||
|
this.resetAttempts(token, name);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resetAllUsers() {
|
||||||
|
try {
|
||||||
|
for (const token in this.users) {
|
||||||
|
this.resetAllAttempts(token);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resetEvent(name) {
|
||||||
|
try {
|
||||||
|
for (const token in this.users) {
|
||||||
|
this.resetAttempts(token, name);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resetUser(token) {
|
||||||
|
try {
|
||||||
|
for (const name in this.users[token].events) {
|
||||||
|
this.resetAttempts(token, name);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.default = RateLimit;
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
const PassCheck = require('./passwordcheck');
|
|
||||||
const JWTAuth = require('./jwt');
|
|
||||||
const RateLimiter = require('./ratelimit');
|
|
||||||
const PassPolicy = require('./passpolicy');
|
|
||||||
const PasswordGenerator = require('./passgen');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
PassCheck,
|
|
||||||
JWTAuth,
|
|
||||||
RateLimiter,
|
|
||||||
PassPolicy,
|
|
||||||
PasswordGenerator
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
/** @type {import('ts-jest').JestConfigWithTsJest} */
|
||||||
|
module.exports = {
|
||||||
|
preset: 'ts-jest',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
};
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
const jwt = require('jsonwebtoken');
|
|
||||||
|
|
||||||
class JwtAuth{
|
|
||||||
constructor(JWTSecretKey){
|
|
||||||
this.JWTSecretKey = JWTSecretKey;
|
|
||||||
this.blacklist = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
generateJWT(payload, settings, secretKey = this.JWTSecretKey) {
|
|
||||||
return jwt.sign(payload, secretKey, settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
verifyJWT(token, secretKey = this.JWTSecretKey) {
|
|
||||||
if(this.blacklist.includes(token)) return { valid: false, message: "Token is blacklisted." };
|
|
||||||
return jwt.verify(token, secretKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
decodeJWT(token) {
|
|
||||||
if(this.blacklist.includes(token)) return { valid: false, message: "Token is blacklisted." };
|
|
||||||
return jwt.decode(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
getJWTExpirationDate(token) {
|
|
||||||
if(this.blacklist.includes(token)) return { valid: false, message: "Token is blacklisted." };
|
|
||||||
const decoded = this.decodeJWT(token);
|
|
||||||
return decoded.exp;
|
|
||||||
}
|
|
||||||
|
|
||||||
isJWTExpired(token) {
|
|
||||||
if(this.blacklist.includes(token)) return { valid: false, message: "Token is blacklisted." };
|
|
||||||
const expirationDate = this.getJWTExpirationDate(token);
|
|
||||||
return expirationDate < Date.now();
|
|
||||||
}
|
|
||||||
|
|
||||||
refreshJWT(token, settings, secretKey = this.JWTSecretKey) {
|
|
||||||
if(this.blacklist.includes(token)) return { valid: false, message: "Token is blacklisted." };
|
|
||||||
const decoded = this.verifyJWT(token, secretKey);
|
|
||||||
const newToken = this.generateJWT(decoded, settings, secretKey);
|
|
||||||
return newToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
async BlackListJWT(token) {
|
|
||||||
//check if token is already blacklisted
|
|
||||||
if (this.blacklist.includes(token)) {
|
|
||||||
return { valid: false, message: "Token is already blacklisted." };
|
|
||||||
}
|
|
||||||
//add token to blacklist
|
|
||||||
this.blacklist.push(token);
|
|
||||||
return { valid: true, message: "Token successfully blacklisted."};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = JwtAuth;
|
|
||||||
Generated
+3607
-4
File diff suppressed because it is too large
Load Diff
+10
-1
@@ -4,7 +4,8 @@
|
|||||||
"description": "auth-guardian a library for enhancing application security",
|
"description": "auth-guardian a library for enhancing application security",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "jest",
|
||||||
|
"build": "tsc"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -20,7 +21,15 @@
|
|||||||
"author": "kaj van schalkwijk",
|
"author": "kaj van schalkwijk",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@types/bcrypt": "^5.0.2",
|
||||||
|
"@types/jsonwebtoken": "^9.0.5",
|
||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
"jsonwebtoken": "^9.0.2"
|
"jsonwebtoken": "^9.0.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/jest": "^29.5.12",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"ts-jest": "^29.1.2",
|
||||||
|
"ts-node": "^10.9.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
const bcrypt = require('bcrypt');
|
|
||||||
const PassPolicy = require('./passpolicy.js');
|
|
||||||
|
|
||||||
class PassCheck{
|
|
||||||
constructor(BcryptSaltRounds, PassPolicyOptions) {
|
|
||||||
this.BcryptSaltRounds = BcryptSaltRounds;
|
|
||||||
this.PassPolicy = new PassPolicy(PassPolicyOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
async verifyPassword(password, hash) {
|
|
||||||
return await bcrypt.compare(password, hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
async hashPassword(password) {
|
|
||||||
const salt = await bcrypt.genSalt(this.BcryptSaltRounds);
|
|
||||||
const hash = await bcrypt.hash(password, salt);
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = PassCheck;
|
|
||||||
-147
@@ -1,147 +0,0 @@
|
|||||||
class RateLimiter {
|
|
||||||
constructor(events){
|
|
||||||
if(events) {
|
|
||||||
for(const event in events){
|
|
||||||
events[event]["attempttimestamp"] = [];
|
|
||||||
events[event]["lastAttempt"] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.events = events;
|
|
||||||
this.users = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
addUser(user){
|
|
||||||
this.users[user] = {};
|
|
||||||
for(const event in this.events){
|
|
||||||
this.users[user][event] = this.events[event];
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.users[user];
|
|
||||||
}
|
|
||||||
|
|
||||||
addEvent(event, max, timespan){
|
|
||||||
this.events[event] = {
|
|
||||||
"max": max,
|
|
||||||
"timespan": timespan,
|
|
||||||
"lastAttempt": 0,
|
|
||||||
"attempttimestamp": [],
|
|
||||||
};
|
|
||||||
|
|
||||||
for(const user in this.users){
|
|
||||||
this.users[user][event] = this.events[event];
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.events[event];
|
|
||||||
}
|
|
||||||
|
|
||||||
removeUser(user){
|
|
||||||
delete this.users[user];
|
|
||||||
}
|
|
||||||
|
|
||||||
attemptEvent(user, event){
|
|
||||||
if(!this.users.has(user)) return "User not found.";
|
|
||||||
if(!this.users[user].has(event)) return "Event not found.";
|
|
||||||
|
|
||||||
const now = Date.now();
|
|
||||||
const max = this.users[user][event].max;
|
|
||||||
const timespan = this.users[user][event].timespan;
|
|
||||||
const attempttimestamp = this.users[user][event].attempttimestamp;
|
|
||||||
|
|
||||||
for(let i = 0; i < attempttimestamp.length; i++){
|
|
||||||
if(attempttimestamp[i] < now - timespan){
|
|
||||||
attempttimestamp.splice(i, 1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const attempts = attempttimestamp.length;
|
|
||||||
|
|
||||||
if(attempts >= max){
|
|
||||||
return [false, "limit reached"];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
attempttimestamp.push(now);
|
|
||||||
this.users[user][event].attempttimestamp = attempttimestamp;
|
|
||||||
this.users[user][event].lastAttempt = now;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resetEventUser(user, event){
|
|
||||||
if(!this.users.has(user)) return "User not found.";
|
|
||||||
if(!this.users[user].has(event)) return "Event not found.";
|
|
||||||
|
|
||||||
this.users[user][event].attempttimestamp = [];
|
|
||||||
this.users[user][event].lastAttempt = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
resetEvent(event){
|
|
||||||
if(!this.events.has(event)) return "Event not found.";
|
|
||||||
for(const user in this.users){
|
|
||||||
if(this.users[user].has(event)){
|
|
||||||
this.users[user][event].attempttimestamp = [];
|
|
||||||
this.users[user][event].lastAttempt = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resetUser(user){
|
|
||||||
if(!this.users.has(user)) return "User not found.";
|
|
||||||
|
|
||||||
for(const event in this.users[user]){
|
|
||||||
this.users[user][event].attempttimestamp = [];
|
|
||||||
this.users[user][event].lastAttempt = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resetAll(){
|
|
||||||
for(const user in this.users){
|
|
||||||
for(const event in this.users[user]){
|
|
||||||
this.users[user][event].attempttimestamp = [];
|
|
||||||
this.users[user][event].lastAttempt = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lastAttempt(user, event){
|
|
||||||
if(!this.users.has(user)) return "User not found.";
|
|
||||||
if(!this.users[user].has(event)) return "Event not found.";
|
|
||||||
|
|
||||||
return this.users[user][event].lastAttempt;
|
|
||||||
}
|
|
||||||
|
|
||||||
userAttempts(user, event){
|
|
||||||
if(!this.users.has(user)) return "User not found.";
|
|
||||||
if(!this.users[user].has(event)) return "Event not found.";
|
|
||||||
|
|
||||||
return this.users[user][event].attempttimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
remainingAttempts(user, event){
|
|
||||||
if(!this.users.has(user)) return "User not found.";
|
|
||||||
if(!this.users[user].has(event)) return "Event not found.";
|
|
||||||
|
|
||||||
const now = Date.now();
|
|
||||||
const max = this.users[user][event].max;
|
|
||||||
const timespan = this.users[user][event].timespan;
|
|
||||||
const attempttimestamp = this.users[user][event].attempttimestamp;
|
|
||||||
|
|
||||||
for(let i = 0; i < attempttimestamp.length; i++){
|
|
||||||
if(attempttimestamp[i] < now - timespan){
|
|
||||||
attempttimestamp.splice(i, 1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const attempts = attempttimestamp.length;
|
|
||||||
|
|
||||||
return max - attempts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = RateLimiter;
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import PassCheck from "./passwordcheck";
|
||||||
|
import JwtAuth from "jsonwebtoken";
|
||||||
|
import PassPolicy from "./passpolicy";
|
||||||
|
import RateLimit from "./ratelimit";
|
||||||
|
import PasswordGenerator from "./passgen";
|
||||||
|
|
||||||
|
export { PassCheck, JwtAuth, PassPolicy, RateLimit, PasswordGenerator };
|
||||||
+138
@@ -0,0 +1,138 @@
|
|||||||
|
import JwtAuth from "./jwt";
|
||||||
|
|
||||||
|
describe('JWT', () => {
|
||||||
|
const jwt = new JwtAuth('secret');
|
||||||
|
const user: {username: string, password: string} = {
|
||||||
|
username: 'test',
|
||||||
|
password: 'test'
|
||||||
|
};
|
||||||
|
|
||||||
|
const settings: {expiresIn: string} = {
|
||||||
|
expiresIn: '1h'
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should generate a JWT', () => {
|
||||||
|
const token = jwt.generateJWT({ username: user.username, password: user.password });
|
||||||
|
expect(token).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should verify a JWT', () => {
|
||||||
|
const token = jwt.generateJWT({ username: user.username, password: user.password });
|
||||||
|
const verified = jwt.verifyJWT(token);
|
||||||
|
//check if username inside the token is equal to the username of the user check this in two lines of code
|
||||||
|
expect(verified).toBeDefined();
|
||||||
|
if (typeof verified !== 'string') {
|
||||||
|
// verified is JwtPayload
|
||||||
|
if(verified == null) fail('Token verification failed');
|
||||||
|
expect(verified.username).toEqual(user.username);
|
||||||
|
} else {
|
||||||
|
// Handle the case where verified is a string (token is blacklisted or invalid)
|
||||||
|
fail('Token verification failed');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should decode a JWT', () => {
|
||||||
|
const token = jwt.generateJWT({ username: user.username, password: user.password });
|
||||||
|
const decoded = jwt.decodeJWT(token);
|
||||||
|
expect(decoded).toBeDefined();
|
||||||
|
if (typeof decoded !== 'string' && decoded !== null) {
|
||||||
|
// verified is JwtPayload
|
||||||
|
expect(decoded.username).toEqual(user.username);
|
||||||
|
} else {
|
||||||
|
// Handle the case where verified is a string (token is blacklisted or invalid)
|
||||||
|
fail('Token verification failed');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get the expiration date of a JWT', () => {
|
||||||
|
const token = jwt.generateJWT({ username: user.username, password: user.password }, settings);
|
||||||
|
const expirationDate = jwt.getJWTExpirationDate(token);
|
||||||
|
expect(expirationDate).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check if a JWT is expired', () => {
|
||||||
|
const token = jwt.generateJWT({ username: user.username, password: user.password }, settings);
|
||||||
|
const isExpired = jwt.isJWTExpired(token);
|
||||||
|
expect(isExpired).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should refresh a JWT', () => {
|
||||||
|
const token = jwt.generateJWT({ username: user.username, password: user.password });
|
||||||
|
const newToken = jwt.refreshJWT(token);
|
||||||
|
expect(newToken).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not verify a JWT with invalid secret key', () => {
|
||||||
|
const token = jwt.generateJWT({ username: user.username, password: user.password });
|
||||||
|
const verified = jwt.verifyJWT(token, 'invalid');
|
||||||
|
expect(verified).toEqual({ valid: false, message: "Token is invalid."});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not refresh a JWT with invalid secret key', () => {
|
||||||
|
const token = jwt.generateJWT({ username: user.username, password: user.password }, settings, 'test');
|
||||||
|
const newToken = jwt.refreshJWT(token, {}, 'invalid');
|
||||||
|
expect(newToken).toEqual({ valid: false, message: "Token is invalid."});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should blacklist a JWT', () => {
|
||||||
|
const token = jwt.generateJWT({ username: 'test12', password: user.password });
|
||||||
|
const blacklisted = jwt.BlackListJWT(token);
|
||||||
|
expect(blacklisted).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not verify a blacklisted JWT', () => {
|
||||||
|
const token = jwt.generateJWT({ username: user.username, password: user.password });
|
||||||
|
jwt.BlackListJWT(token);
|
||||||
|
const verified = jwt.verifyJWT(token);
|
||||||
|
expect(verified).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not decode a blacklisted JWT', () => {
|
||||||
|
const token = jwt.generateJWT({ username: user.username, password: user.password });
|
||||||
|
jwt.BlackListJWT(token);
|
||||||
|
const decoded = jwt.decodeJWT(token);
|
||||||
|
expect(decoded).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not get the expiration date of a blacklisted JWT', () => {
|
||||||
|
const token = jwt.generateJWT({ username: user.username, password: user.password });
|
||||||
|
jwt.BlackListJWT(token);
|
||||||
|
const expirationDate = jwt.getJWTExpirationDate(token);
|
||||||
|
expect(expirationDate).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not check if a blacklisted JWT is expired', () => {
|
||||||
|
const token = jwt.generateJWT({ username: user.username, password: user.password });
|
||||||
|
jwt.BlackListJWT(token);
|
||||||
|
const isExpired = jwt.isJWTExpired(token);
|
||||||
|
expect(isExpired).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not refresh a blacklisted JWT', () => {
|
||||||
|
const token = jwt.generateJWT({ username: user.username, password: user.password });
|
||||||
|
jwt.BlackListJWT(token);
|
||||||
|
const newToken = jwt.refreshJWT(token);
|
||||||
|
expect(newToken).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not blacklist a blacklisted JWT', () => {
|
||||||
|
const token = jwt.generateJWT({ username: user.username, password: user.password });
|
||||||
|
jwt.BlackListJWT(token);
|
||||||
|
const blacklisted = jwt.BlackListJWT(token);
|
||||||
|
expect(blacklisted).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not blacklist a blacklisted JWT', () => {
|
||||||
|
const token = jwt.generateJWT({ username: user.username, password: user.password });
|
||||||
|
const verify = jwt.verifyJWT(token, 'invalid');
|
||||||
|
const blacklisted = jwt.IsBlackListed(token);
|
||||||
|
expect(blacklisted).toEqual({ valid: true, message: "Token is blacklisted." })
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove a JWT from the blacklist', () => {
|
||||||
|
const token = jwt.generateJWT({ username: user.username, password: user.password });
|
||||||
|
jwt.BlackListJWT(token);
|
||||||
|
const removed = jwt.RemoveFromBlackList(token);
|
||||||
|
expect(removed).toEqual({ valid: true, message: "Token successfully removed from blacklist." });
|
||||||
|
});
|
||||||
|
});
|
||||||
+106
@@ -0,0 +1,106 @@
|
|||||||
|
import * as jwt from 'jsonwebtoken';
|
||||||
|
|
||||||
|
export default class JwtAuth{
|
||||||
|
private JWTSecretKey: string;
|
||||||
|
private blacklist: string[];
|
||||||
|
|
||||||
|
public constructor(JWTSecretKey: string){
|
||||||
|
this.JWTSecretKey = JWTSecretKey;
|
||||||
|
this.blacklist = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
generateJWT(payload: { [key: string]: any}, settings: jwt.SignOptions = {}, secretKey = this.JWTSecretKey): string {
|
||||||
|
return jwt.sign(payload, secretKey, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyJWT(token: string, secretKey = this.JWTSecretKey) {
|
||||||
|
try{
|
||||||
|
if(this.blacklist.includes(token)) return { valid: false, message: "Token is blacklisted." };
|
||||||
|
const vertoken = jwt.verify(token, secretKey);
|
||||||
|
|
||||||
|
if(vertoken instanceof Object) {
|
||||||
|
return vertoken;
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return { valid: false, message: "Token is invalid." };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decodeJWT(token: string) {
|
||||||
|
if(this.blacklist.includes(token)) return { valid: false, message: "Token is blacklisted." };
|
||||||
|
return jwt.decode(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
getJWTExpirationDate(token: string) {
|
||||||
|
if(this.blacklist.includes(token)) return { valid: false, message: "Token is blacklisted." };
|
||||||
|
const decoded = this.decodeJWT(token) as { [key: string]: any };
|
||||||
|
return decoded.exp;
|
||||||
|
}
|
||||||
|
|
||||||
|
isJWTExpired(token: string) {
|
||||||
|
if(this.blacklist.includes(token)) return { valid: false, message: "Token is blacklisted." };
|
||||||
|
const expirationDate = this.getJWTExpirationDate(token);
|
||||||
|
return expirationDate < Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshJWT(token: string, settings: jwt.SignOptions = {}, secretKey = this.JWTSecretKey) {
|
||||||
|
if(this.blacklist.includes(token)) return { valid: false, message: "Token is blacklisted." };
|
||||||
|
const decoded = this.verifyJWT(token, secretKey);
|
||||||
|
|
||||||
|
if(decoded instanceof Object) {
|
||||||
|
if(decoded.valid == false) return { valid: false, message: "Token is invalid." };
|
||||||
|
|
||||||
|
let payLoadArray: { [key: string]: any } = {};
|
||||||
|
|
||||||
|
if (decoded instanceof Object) {
|
||||||
|
payLoadArray = decoded as { [key: string]: any };
|
||||||
|
} else {
|
||||||
|
return { valid: false, message: "Token is invalid." };
|
||||||
|
}
|
||||||
|
|
||||||
|
const newToken = this.generateJWT(payLoadArray, settings, secretKey);
|
||||||
|
return newToken;
|
||||||
|
}
|
||||||
|
return { valid: false, message: "Token is invalid." };
|
||||||
|
}
|
||||||
|
|
||||||
|
BlackListJWT(token: string) {
|
||||||
|
//check if token is already blacklisted
|
||||||
|
if (this.blacklist.includes(token)) {
|
||||||
|
return { valid: false, message: "Token is already blacklisted." };
|
||||||
|
}
|
||||||
|
//add token to blacklist
|
||||||
|
this.blacklist.push(token);
|
||||||
|
return { valid: true, message: "Token successfully blacklisted."};
|
||||||
|
}
|
||||||
|
|
||||||
|
ClearBlackList() {
|
||||||
|
//clear blacklist
|
||||||
|
this.blacklist = [];
|
||||||
|
return { valid: true, message: "Blacklist successfully cleared."};
|
||||||
|
}
|
||||||
|
|
||||||
|
GetBlackList() {
|
||||||
|
//return blacklist
|
||||||
|
return this.blacklist;
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoveFromBlackList(token: string) {
|
||||||
|
//remove token from blacklist
|
||||||
|
if (this.blacklist.includes(token)) {
|
||||||
|
this.blacklist = this.blacklist.filter((item) => item !== token);
|
||||||
|
return { valid: true, message: "Token successfully removed from blacklist."};
|
||||||
|
}
|
||||||
|
return { valid: false, message: "Token is not blacklisted." };
|
||||||
|
}
|
||||||
|
|
||||||
|
IsBlackListed(token: string) {
|
||||||
|
//check if token is blacklisted
|
||||||
|
if (this.blacklist.includes(token)) {
|
||||||
|
return { valid: true, message: "Token is blacklisted." };
|
||||||
|
}
|
||||||
|
return { valid: false, message: "Token is not blacklisted." };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import PassCheck from "./passwordcheck";
|
||||||
|
|
||||||
|
describe('PassCheck', () => {
|
||||||
|
const passCheck = new PassCheck(10, {
|
||||||
|
minLength: 6,
|
||||||
|
maxLength: 32,
|
||||||
|
minLower: 2,
|
||||||
|
minUpper: 2,
|
||||||
|
minNum: 2,
|
||||||
|
minSpecial: 1,
|
||||||
|
specialChars: "!@#$%^&*()_+~`|}{[]:;?><,./-=",
|
||||||
|
});
|
||||||
|
|
||||||
|
const pass = "Test123!";
|
||||||
|
|
||||||
|
it('should hash a password', async () => {
|
||||||
|
const hash = await passCheck.hashPassword(pass);
|
||||||
|
expect(hash).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should verify a password', async () => {
|
||||||
|
const hash = await passCheck.hashPassword(pass);
|
||||||
|
const result = await passCheck.verifyPassword(pass, hash);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not verify a wrong password', async () => {
|
||||||
|
const hash = await passCheck.hashPassword(pass);
|
||||||
|
const result = await passCheck.verifyPassword("wrongpass", hash);
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not verify a wrong hash', async () => {
|
||||||
|
const result = await passCheck.verifyPassword(pass, "wronghash");
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not verify a wrong password and hash', async () => {
|
||||||
|
const result = await passCheck.verifyPassword("wrongpass",
|
||||||
|
"wronghash");
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import PasswordGenerator from "./passgen";
|
||||||
|
|
||||||
|
describe('PasswordGenerator', () => {
|
||||||
|
const passgen = new PasswordGenerator({
|
||||||
|
minLength: 6,
|
||||||
|
maxLength: 32,
|
||||||
|
minLower: 2,
|
||||||
|
minUpper: 2,
|
||||||
|
minNum: 2,
|
||||||
|
minSpecial: 3,
|
||||||
|
specialChars: "!@#$%^&*()_+~`|}{[]:;?><,./-=",
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate a password', async () => {
|
||||||
|
const pass = await passgen.Generate();
|
||||||
|
expect(pass).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate a password with a specific length', async () => {
|
||||||
|
const pass = await passgen.Generate(12);
|
||||||
|
expect(pass.length).toEqual(12);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
class PasswordGenerator{
|
export default class PasswordGenerator{
|
||||||
constructor(options){
|
private options: { minLength: number, maxLength: number, minLower: number, minUpper: number, minNum: number, minSpecial: number, specialChars: string };
|
||||||
|
constructor(options: { minLength: number, maxLength: number, minLower: number, minUpper: number, minNum: number, minSpecial: number, specialChars: string }){
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
minLength: 6,
|
minLength: 6,
|
||||||
maxLength: 32,
|
maxLength: 32,
|
||||||
@@ -12,7 +13,7 @@ class PasswordGenerator{
|
|||||||
this.options = { ...defaultOptions, ...options };
|
this.options = { ...defaultOptions, ...options };
|
||||||
}
|
}
|
||||||
|
|
||||||
async Generate(){
|
Generate(length: number = 0){
|
||||||
// Generate random password that complies with the options
|
// Generate random password that complies with the options
|
||||||
const { minLength, maxLength, minLower, minUpper, minNum, minSpecial, specialChars } = this.options;
|
const { minLength, maxLength, minLower, minUpper, minNum, minSpecial, specialChars } = this.options;
|
||||||
|
|
||||||
@@ -24,7 +25,9 @@ class PasswordGenerator{
|
|||||||
|
|
||||||
// Generate password
|
// Generate password
|
||||||
//generate random length
|
//generate random length
|
||||||
let length = Math.floor(Math.random() * (maxLength - minLength + 1) + minLength);
|
if(length === undefined || length < minLength || length > maxLength || length === 0){
|
||||||
|
length = Math.floor(Math.random() * (maxLength - minLength + 1) + minLength);
|
||||||
|
}
|
||||||
//generate random length for each character type
|
//generate random length for each character type
|
||||||
let lowerLength = Math.floor(Math.random() * (length - minLower - minUpper - minNum - minSpecial + 1) + minLower);
|
let lowerLength = Math.floor(Math.random() * (length - minLower - minUpper - minNum - minSpecial + 1) + minLower);
|
||||||
let upperLength = Math.floor(Math.random() * (length - lowerLength - minUpper - minNum - minSpecial + 1) + minUpper);
|
let upperLength = Math.floor(Math.random() * (length - lowerLength - minUpper - minNum - minSpecial + 1) + minUpper);
|
||||||
@@ -50,7 +53,7 @@ class PasswordGenerator{
|
|||||||
|
|
||||||
//shuffle password
|
//shuffle password
|
||||||
pass = pass.split('').sort(function(){return 0.5-Math.random()}).join('');
|
pass = pass.split('').sort(function(){return 0.5-Math.random()}).join('');
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = PasswordGenerator;
|
return pass;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import PassPolicy from "./passpolicy";
|
||||||
|
|
||||||
|
describe("PassPolicy", () => {
|
||||||
|
const passPolicy = new PassPolicy({
|
||||||
|
minLength: 6,
|
||||||
|
maxLength: 32,
|
||||||
|
minLower: 2,
|
||||||
|
minUpper: 2,
|
||||||
|
minNum: 2,
|
||||||
|
minSpecial: 1,
|
||||||
|
specialChars: "!@#$%^&*()_+~`|}{[]:;?><,./-=",
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should validate a password", () => {
|
||||||
|
const pass = "TesT123!";
|
||||||
|
const result = passPolicy.validate(pass);
|
||||||
|
expect(result.valid).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should check the difference between two passwords", () => {
|
||||||
|
const pass1 = "Test123!";
|
||||||
|
const pass2 = "Test123456!";
|
||||||
|
const result = passPolicy.CheckDifference(pass1, pass2 , 4);
|
||||||
|
expect(result.valid).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
export default class PassPolicy {
|
||||||
|
private options: { minLength: number, maxLength: number, minLower: number, minUpper: number, minNum: number, minSpecial: number, specialChars: string };
|
||||||
|
constructor(options: { minLength: number, maxLength: number, minLower: number, minUpper: number, minNum: number, minSpecial: number, specialChars: string }){
|
||||||
|
const defaultOptions = {
|
||||||
|
minLength: 6,
|
||||||
|
maxLength: 32,
|
||||||
|
minLower: 2,
|
||||||
|
minUpper: 2,
|
||||||
|
minNum: 2,
|
||||||
|
minSpecial: 3,
|
||||||
|
specialChars: "!@#$%^&*()_+~`|}{[]:;?><,./-=",
|
||||||
|
};
|
||||||
|
this.options = { ...defaultOptions, ...options };
|
||||||
|
}
|
||||||
|
|
||||||
|
validate(password: string) {
|
||||||
|
const { minLength, maxLength, minLower, minUpper, minNum, minSpecial, specialChars } = this.options;
|
||||||
|
|
||||||
|
if (password.length < minLength || password.length > maxLength) {
|
||||||
|
return { valid: false, message: "Password length does not meet requirements." };
|
||||||
|
}
|
||||||
|
|
||||||
|
const lowerRegex = /[a-z]/g;
|
||||||
|
const upperRegex = /[A-Z]/g;
|
||||||
|
const numRegex = /[0-9]/g;
|
||||||
|
const specialRegex = new RegExp(`[${specialChars.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&")}]`, "g");
|
||||||
|
|
||||||
|
const lowerCount = (password.match(lowerRegex) || []).length;
|
||||||
|
const upperCount = (password.match(upperRegex) || []).length;
|
||||||
|
const numCount = (password.match(numRegex) || []).length;
|
||||||
|
const specialCount = (password.match(specialRegex) || []).length;
|
||||||
|
|
||||||
|
if (lowerCount < minLower) {
|
||||||
|
return { valid: false, message: "Password must contain at least " + minLower + " lowercase letter(s)." };
|
||||||
|
}
|
||||||
|
if (upperCount < minUpper) {
|
||||||
|
return { valid: false, message: "Password must contain at least " + minUpper + " uppercase letter(s)." };
|
||||||
|
}
|
||||||
|
if (numCount < minNum) {
|
||||||
|
return { valid: false, message: "Password must contain at least " + minNum + " digit(s)." };
|
||||||
|
}
|
||||||
|
if (specialCount < minSpecial) {
|
||||||
|
return { valid: false, message: "Password must contain at least " + minSpecial + " special character(s)." };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { valid: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckDifference(newPassword: string, oldPassword: string, neededDifference: number = 3) {
|
||||||
|
//check if new password is different from old password
|
||||||
|
if (newPassword === oldPassword) {
|
||||||
|
return { valid: false, message: "New password must be different from old password." };
|
||||||
|
}
|
||||||
|
|
||||||
|
//check how many characters are different
|
||||||
|
let diffCount = 0;
|
||||||
|
//check if new password is longer than old password
|
||||||
|
if (newPassword.length > oldPassword.length) {
|
||||||
|
for (let i = 0; i < oldPassword.length; i++) {
|
||||||
|
if (newPassword[i] != oldPassword[i]) {
|
||||||
|
diffCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
diffCount += newPassword.length - oldPassword.length;
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < newPassword.length; i++) {
|
||||||
|
if (newPassword[i] != oldPassword[i]) {
|
||||||
|
diffCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
diffCount += oldPassword.length - newPassword.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
//check if difference is enough
|
||||||
|
if (diffCount < neededDifference) {
|
||||||
|
return { valid: false, message: "New password must be different from old password by at least " + neededDifference + " characters." };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { valid: true };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import bcrypt from 'bcrypt';
|
||||||
|
import PassPolicy from './passpolicy';
|
||||||
|
|
||||||
|
export default class PassCheck{
|
||||||
|
private BcryptSaltRounds: number;
|
||||||
|
private PassPolicy: PassPolicy;
|
||||||
|
constructor(BcryptSaltRounds: number, PassPolicyOptions: { minLength: number, maxLength: number, minLower: number, minUpper: number, minNum: number, minSpecial: number, specialChars: string }) {
|
||||||
|
this.BcryptSaltRounds = BcryptSaltRounds;
|
||||||
|
this.PassPolicy = new PassPolicy(PassPolicyOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyPassword(password: string, hash: string) {
|
||||||
|
return await bcrypt.compare(password, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
async hashPassword(password: string) {
|
||||||
|
const salt = await bcrypt.genSalt(this.BcryptSaltRounds);
|
||||||
|
const hash = await bcrypt.hash(password, salt);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,199 @@
|
|||||||
|
type Event = {
|
||||||
|
name: string;
|
||||||
|
cooldown: number;
|
||||||
|
maxAttempts: number;
|
||||||
|
lastAttempt?: number;
|
||||||
|
attempts?: number[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type user = {
|
||||||
|
token: string;
|
||||||
|
events: { [name: string]: Event };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class RateLimit {
|
||||||
|
constructor(public users: { [token: string]: user} = {}, public events: { [name: string]: Event} = {}) {
|
||||||
|
this.users = users;
|
||||||
|
this.events = events;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addEvent(event: Event) {
|
||||||
|
try{
|
||||||
|
this.events[event.name] = event;
|
||||||
|
|
||||||
|
//add event to all users
|
||||||
|
for (const token in this.users) {
|
||||||
|
this.users[token].events[event.name] = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeEvent(name: string) {
|
||||||
|
try{
|
||||||
|
delete this.events[name];
|
||||||
|
|
||||||
|
//remove event from all users
|
||||||
|
for (const token in this.users) {
|
||||||
|
delete this.users[token].events[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public addUser(token: string) {
|
||||||
|
try{
|
||||||
|
this.users[token] = { token, events: this.events };
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeUser(token: string) {
|
||||||
|
try{
|
||||||
|
delete this.users[token];
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public attempt(token: string, name: string) {
|
||||||
|
try{
|
||||||
|
if (!this.users[token] || !this.users[token].events[name]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const event = this.users[token].events[name];
|
||||||
|
|
||||||
|
if (event.attempts === undefined) {
|
||||||
|
event.attempts = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
if (event.lastAttempt && now - event.lastAttempt < event.cooldown) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.lastAttempt = now;
|
||||||
|
event.attempts.push(now);
|
||||||
|
|
||||||
|
if (event.attempts.length > event.maxAttempts) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getEvents() {
|
||||||
|
return this.events;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getUsers() {
|
||||||
|
return this.users;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getEvent(name: string) {
|
||||||
|
return this.events[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
public getUser(token: string) {
|
||||||
|
return this.users[token];
|
||||||
|
}
|
||||||
|
|
||||||
|
public remainingAttempts(token: string, name: string) {
|
||||||
|
try{
|
||||||
|
if (!this.users[token] || !this.users[token].events[name]) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const event = this.users[token].events[name];
|
||||||
|
|
||||||
|
if (event.attempts === undefined) {
|
||||||
|
event.attempts = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return event.maxAttempts - event.attempts.length;
|
||||||
|
} catch (error) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public resetAttempts(token: string, name: string) {
|
||||||
|
try{
|
||||||
|
if (!this.users[token] || !this.users[token].events[name]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const event = this.users[token].events[name];
|
||||||
|
|
||||||
|
event.attempts = [];
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public resetAllAttempts(token: string) {
|
||||||
|
try{
|
||||||
|
if (!this.users[token]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const name in this.users[token].events) {
|
||||||
|
this.resetAttempts(token, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public resetAllUsers() {
|
||||||
|
try{
|
||||||
|
for (const token in this.users) {
|
||||||
|
this.resetAllAttempts(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public resetEvent(name: string) {
|
||||||
|
try{
|
||||||
|
for (const token in this.users) {
|
||||||
|
this.resetAttempts(token, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public resetUser(token: string) {
|
||||||
|
try{
|
||||||
|
for (const name in this.users[token].events) {
|
||||||
|
this.resetAttempts(token, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+111
@@ -0,0 +1,111 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||||
|
|
||||||
|
/* Projects */
|
||||||
|
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||||
|
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||||
|
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||||
|
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||||
|
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||||
|
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||||
|
|
||||||
|
/* Language and Environment */
|
||||||
|
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||||
|
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||||
|
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||||
|
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
||||||
|
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||||
|
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||||
|
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||||
|
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||||
|
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||||
|
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||||
|
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||||
|
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||||
|
|
||||||
|
/* Modules */
|
||||||
|
"module": "commonjs", /* Specify what module code is generated. */
|
||||||
|
"rootDir": "./src", /* Specify the root folder within your source files. */
|
||||||
|
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||||
|
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||||
|
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||||
|
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||||
|
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||||
|
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||||
|
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||||
|
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||||
|
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
|
||||||
|
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
||||||
|
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
||||||
|
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
||||||
|
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||||
|
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
||||||
|
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||||
|
|
||||||
|
/* JavaScript Support */
|
||||||
|
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||||
|
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||||
|
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||||
|
|
||||||
|
/* Emit */
|
||||||
|
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||||
|
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||||
|
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||||
|
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||||
|
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||||
|
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||||
|
"outDir": "./dist", /* Specify an output folder for all emitted files. */
|
||||||
|
// "removeComments": true, /* Disable emitting comments. */
|
||||||
|
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||||
|
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||||
|
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
||||||
|
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||||
|
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||||
|
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||||
|
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||||
|
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||||
|
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||||
|
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||||
|
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||||
|
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||||
|
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||||
|
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||||
|
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||||
|
|
||||||
|
/* Interop Constraints */
|
||||||
|
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||||
|
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
|
||||||
|
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||||
|
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||||
|
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||||
|
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||||
|
|
||||||
|
/* Type Checking */
|
||||||
|
"strict": true, /* Enable all strict type-checking options. */
|
||||||
|
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||||
|
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||||
|
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||||
|
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||||
|
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||||
|
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||||
|
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||||
|
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||||
|
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||||
|
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||||
|
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||||
|
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||||
|
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||||
|
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||||
|
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||||
|
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||||
|
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||||
|
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||||
|
|
||||||
|
/* Completeness */
|
||||||
|
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||||
|
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules", "**/*.test.ts"],
|
||||||
|
"include": ["src/**/*.ts"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user