feat: add bcrypt for password hashing and comparison utilities
Some checks failed
GBCI / build (push) Failing after 3m46s

This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2025-04-17 21:45:46 -03:00
parent 31b7d5d59e
commit ea224b4937
10 changed files with 54 additions and 9 deletions

21
package-lock.json generated
View file

@ -54,6 +54,7 @@
"async-promises": "0.2.3",
"async-retry": "1.3.3",
"basic-auth": "2.0.1",
"bcrypt": "^5.1.1",
"billboard.js": "3.13.0",
"bluebird": "3.7.2",
"body-parser": "1.20.2",
@ -12921,6 +12922,20 @@
"node": ">=10.0.0"
}
},
"node_modules/bcrypt": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz",
"integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@mapbox/node-pre-gyp": "^1.0.11",
"node-addon-api": "^5.0.0"
},
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/bcrypt-pbkdf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
@ -12930,6 +12945,12 @@
"tweetnacl": "^0.14.3"
}
},
"node_modules/bcrypt/node_modules/node-addon-api": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
"integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==",
"license": "MIT"
},
"node_modules/bcryptjs": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",

View file

@ -114,6 +114,7 @@
"async-promises": "0.2.3",
"async-retry": "1.3.3",
"basic-auth": "2.0.1",
"bcrypt": "^5.1.1",
"billboard.js": "3.13.0",
"bluebird": "3.7.2",
"body-parser": "1.20.2",

View file

@ -47,6 +47,7 @@ import { SecService } from '../../security.gbapp/services/SecService.js';
import { GBConfigService } from '../../core.gbapp/services/GBConfigService.js';
import { GBServer } from '../../../src/app.js';
import { GBLogEx } from '../../core.gbapp/services/GBLogEx.js';
import { GBUtil } from 'src/util.js';
class AdminDialog extends IGBDialog {
@ -86,7 +87,7 @@ class AdminDialog extends IGBDialog {
const locale = step.context.activity.locale;
const sensitive = step.context.activity['originalText'];
if (sensitive === min.instance.adminPass) {
if (await GBUtil.comparePassword( sensitive, min.instance.adminPass)) {
await min.conversationalService.sendText(min, step, Messages[locale].welcome);
return await step.endDialog(true);
@ -120,7 +121,7 @@ class AdminDialog extends IGBDialog {
const locale = step.context.activity.locale;
const sensitive = step.context.activity['originalText'];
if (sensitive === min.instance.adminPass) {
if (await GBUtil.comparePassword( sensitive, min.instance.adminPass)) {
await min.conversationalService.sendText(min, step, Messages[locale].welcome);
return await min.conversationalService.prompt(min, step, Messages[locale].which_task);

View file

@ -111,7 +111,7 @@ export class StartDialog {
instance.cloudLocation = location;
instance.marketplaceId = appId;
instance.marketplacePassword = appPassword;
instance.adminPass = GBAdminService.getRndPassword();
instance.adminPass = await GBUtil.hashPassword(GBAdminService.getRndPassword());
return { instance, credentials, subscriptionId , installationDeployer};
}

View file

@ -518,7 +518,7 @@ export class AzureDeployerService implements IGBInstallationDeployer {
instance.nlpAuthoringKey = authoringKey;
instance.marketplaceId = appId;
instance.marketplacePassword = appPassword;
instance.adminPass = GBAdminService.getRndPassword();
instance.adminPass = await GBUtil.hashPassword(GBAdminService.getRndPassword());
const credentials = await GBAdminService.getADALCredentialsFromUsername(username, password);
// tslint:disable-next-line:no-http-string
@ -986,7 +986,6 @@ export class AzureDeployerService implements IGBInstallationDeployer {
appSettings: [
{ name: 'WEBSITES_CONTAINER_START_TIME_LIMIT', value: `${WebSiteResponseTimeout}` },
{ name: 'WEBSITE_NODE_DEFAULT_VERSION', value: await GBAdminService.getNodeVersion() },
{ name: 'ADMIN_PASS', value: `${instance.adminPass}` },
{ name: 'BOT_ID', value: `${instance.botId}` },
{ name: 'CLOUD_SUBSCRIPTIONID', value: `${instance.cloudSubscriptionId}` },
{ name: 'CLOUD_LOCATION', value: `${instance.cloudLocation}` },

View file

@ -323,7 +323,6 @@ export class GBCoreService implements IGBCoreService {
public async writeEnv(instance: IGBInstance) {
const env = `
ADDITIONAL_DEPLOY_PATH=
ADMIN_PASS=${instance.adminPass}
BOT_ID=${instance.botId}
CLOUD_SUBSCRIPTIONID=${instance.cloudSubscriptionId}
CLOUD_LOCATION=${instance.cloudLocation}

View file

@ -241,7 +241,7 @@ export class GBDeployer implements IGBDeployer {
instance.marketplacePassword = await service.createApplicationSecret(accessToken, (application as any).id);
}
instance.adminPass = GBAdminService.getRndPassword();
instance.adminPass =await GBUtil.hashPassword( GBAdminService.getRndPassword());
instance.title = botId;
instance.activationCode = instance.botId.substring(0, 15);
instance.state = 'active';

View file

@ -78,7 +78,7 @@ export class GBImporter {
if (!instance) {
instance = <IGBInstance>{};
instance.state = 'active';
instance.adminPass = GBConfigService.get('ADMIN_PASS');
instance.adminPass = await GBUtil.hashPassword( GBConfigService.get('ADMIN_PASS'));
instance.botId = GBConfigService.get('BOT_ID');
instance.cloudSubscriptionId = GBConfigService.get('CLOUD_SUBSCRIPTIONID');
instance.cloudLocation = GBConfigService.get('CLOUD_LOCATION');

View file

@ -159,7 +159,6 @@ export class MainService {
<br/>
<br/> Digite /publish do seu WhatsApp para publicar os pacotes. Seu número está autorizado na pasta ${botName}.gbot/Config.xlsx
<br/>
<br/> Guarde a senha raiz: <b>${instance.adminPass}</b> em um local seguro, use-a para realizar o /publish via Web (WhatsApp dispensa senha).
<br/>
<br/>O arquivo .zip em anexo pode ser importado no Teams conforme instruções em:
<br/><a href="https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/deploy-and-publish/apps-upload">https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/deploy-and-publish/apps-upload</a>.

View file

@ -12,6 +12,8 @@ import SwaggerClient from 'swagger-client';
import fs from 'fs/promises';
import { GBConfigService } from '../packages/core.gbapp/services/GBConfigService.js';
import path from 'path';
import bcrypt from 'bcrypt';
const saltRounds = 10; // The higher the number, the more secure but slower
import { VerbosityLevel, getDocument } from 'pdfjs-dist/legacy/build/pdf.mjs';
import urljoin from 'url-join';
import { GBAdminService } from '../packages/admin.gbapp/services/GBAdminService.js';
@ -27,6 +29,29 @@ import { QueryTypes } from '@sequelize/core';
* Utility class containing various helper functions for the General Bots project.
*/
export class GBUtil {
// When creating/updating a user (hashing before saving to DB)
public async static hashPassword(password) {
try {
const hash = await bcrypt.hash(password, saltRounds);
return hash;
} catch (err) {
console.error('Error hashing password:', err);
throw err;
}
}
// When comparing passwords (like during login)
public async static comparePassword(inputPassword, hashedPassword) {
try {
return await bcrypt.compare(inputPassword, hashedPassword);
} catch (err) {
console.error('Error comparing passwords:', err);
throw err;
}
}
/**
* Repeats a character a specified number of times.
* @param {string} chr - The character to repeat.