Set tenantID param at first position in Cypher class + Fix sub-modules setting access
MODIFY
package.json
package.json
"mochatest:ocpp": "cross-env TS_NODE_FILES=true mocha test/**/OCPP*Test.ts",
"mochatest:authentication": "cross-env TS_NODE_FILES=true mocha test/**/AuthenticationTest.ts",
"mochatest:createContext": "cross-env TS_NODE_FILES=true mocha test/**/ContextBuilderRunner.ts",
- "mochatest:encryptionSetting": "cross-env TS_NODE_FILES=true mocha test/**/EncryptionSettingTest.ts",
+ "mochatest:encryption": "cross-env TS_NODE_FILES=true mocha test/**/EncryptionSettingTest.ts",
"mochatest:filter": "cross-env TS_NODE_FILES=true mocha test/**/FilterTest.ts",
"mochatest:tenantsetting": "cross-env TS_NODE_FILES=true mocha test/**/ComponentActivationTenantTest.ts",
"mochatest:billing": "cross-env TS_NODE_FILES=true mocha test/**/BillingTest.ts",

MODIFY
src/integration/billing/stripe/StripeBillingIntegration.ts
src/integration/billing/stripe/StripeBillingIntegration.ts
@@ -693,7 +693,7 @@ export default class StripeBillingIntegration extends BillingIntegration<StripeB
private async initializeStripe() {
this.settings.currency = this.stripeSettings.currency;
if (this.settings.secretKey) {
- this.settings.secretKey = await Cypher.decrypt(this.stripeSettings.secretKey, this.tenantID);
+ this.settings.secretKey = await Cypher.decrypt(this.tenantID, this.stripeSettings.secretKey);
}
// Currently the public key is not encrypted
this.stripe = new Stripe(this.settings.secretKey);

MODIFY
src/integration/pricing/PricingIntegration.ts
src/integration/pricing/PricingIntegration.ts
@@ -16,9 +16,9 @@ export default abstract class PricingIntegration<T extends PricingSetting> {
return this.setting;
}
- abstract startSession(transaction: Transaction, consumptionData: Consumption): Promise<PricedConsumption>;
+ public abstract startSession(transaction: Transaction, consumptionData: Consumption): Promise<PricedConsumption>;
- abstract updateSession(transaction: Transaction, consumptionData: Consumption): Promise<PricedConsumption>;
+ public abstract updateSession(transaction: Transaction, consumptionData: Consumption): Promise<PricedConsumption>;
- abstract stopSession(transaction: Transaction, consumptionData: Consumption): Promise<PricedConsumption>;
+ public abstract stopSession(transaction: Transaction, consumptionData: Consumption): Promise<PricedConsumption>;
}

MODIFY
src/migration/tasks/AddSensitiveDataInSettingsTask.ts
src/migration/tasks/AddSensitiveDataInSettingsTask.ts
@@ -34,7 +34,7 @@ export default class AddSensitiveDataInSettingsTask extends MigrationTask {
setting.sensitiveData = ['content.concur.clientSecret'];
// Encrypt
if (setting.content.concur.clientSecret) {
- setting.content.concur.clientSecret = await Cypher.encrypt(setting.content.concur.clientSecret, tenant.id);
+ setting.content.concur.clientSecret = await Cypher.encrypt(tenant.id, setting.content.concur.clientSecret);
} else {
setting.content.concur.clientSecret = '';
}
@@ -42,7 +42,7 @@ export default class AddSensitiveDataInSettingsTask extends MigrationTask {
} else if (setting.content.type === PricingSettingsType.CONVERGENT_CHARGING) {
setting.sensitiveData = ['content.convergentCharging.password'];
if (setting.content.convergentCharging.password) {
- setting.content.convergentCharging.password = await Cypher.encrypt(setting.content.convergentCharging.password, tenant.id);
+ setting.content.convergentCharging.password = await Cypher.encrypt(tenant.id, setting.content.convergentCharging.password);
} else {
setting.content.convergentCharging.password = '';
}

MODIFY
src/migration/tasks/MigrateCryptoSettingsFromConfigToDBTask.ts
src/migration/tasks/MigrateCryptoSettingsFromConfigToDBTask.ts
-import { CryptoKeyProperties, CryptoSettingsType, KeySetting } from '../../types/Setting';
+import { CryptoKeyProperties, CryptoKeySetting, CryptoSettingsType } from '../../types/Setting';
import Configuration from '../../utils/Configuration';
import Constants from '../../utils/Constants';
@@ -12,7 +12,6 @@ import Utils from '../../utils/Utils';
export default class MigrateCryptoSettingsFromConfigToDBTask extends MigrationTask {
public async migrate(): Promise<void> {
const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT);
-
for (const tenant of tenants.result) {
await this.migrateTenant(tenant);
}
@@ -22,7 +21,7 @@ export default class MigrateCryptoSettingsFromConfigToDBTask extends MigrationTa
// Crypto Key from config file
const configCryptoKey: string = Configuration.getCryptoConfig().key;
// Crypto Key Properties from config file
- const configCryptoKeyProperties: CryptoKeyProperties = Utils.parseConfigCryptoAlgorithm(Configuration.getCryptoConfig().algorithm);
+ const configCryptoKeyProperties = Utils.parseConfigCryptoAlgorithm(Configuration.getCryptoConfig().algorithm);
// Crypto Key Setting from db
const keySettings = await SettingStorage.getCryptoSettings(tenant.id);
// If no Crypto Key Setting exist, initialize them with Crypto Key from config file
@@ -35,7 +34,7 @@ export default class MigrateCryptoSettingsFromConfigToDBTask extends MigrationTa
key: configCryptoKey,
keyProperties: configCryptoKeyProperties,
}
- } as KeySetting;
+ } as CryptoKeySetting;
await SettingStorage.saveCryptoSettings(tenant.id, keySettingToSave);
}
}

MODIFY
src/server/rest/v1/service/SettingService.ts
src/server/rest/v1/service/SettingService.ts
@@ -116,7 +116,7 @@ export default class SettingService {
// Filter
const filteredRequest = SettingSecurity.filterSettingCreateRequest(req.body);
// Process the sensitive data if any
- await Cypher.encryptSensitiveDataInJSON(filteredRequest, req.user.tenantID);
+ await Cypher.encryptSensitiveDataInJSON(req.user.tenantID, filteredRequest);
// Update timestamp
filteredRequest.createdBy = { 'id': req.user.id };
filteredRequest.createdOn = new Date();
@@ -177,14 +177,14 @@ export default class SettingService {
const hashedValueInDB = Cypher.hash(valueInDb);
if (valueInRequest !== hashedValueInDB) {
// Yes: Encrypt
- _.set(settingUpdate, property, await Cypher.encrypt(valueInRequest, req.user.tenantID));
+ _.set(settingUpdate, property, await Cypher.encrypt(req.user.tenantID, valueInRequest));
} else {
// No: Put back the encrypted value
_.set(settingUpdate, property, valueInDb);
}
} else {
// Value in db is empty then encrypt
- _.set(settingUpdate, property, await Cypher.encrypt(valueInRequest, req.user.tenantID));
+ _.set(settingUpdate, property, await Cypher.encrypt(req.user.tenantID, valueInRequest));
}
}
}

MODIFY
src/server/rest/v1/service/TenantService.ts
src/server/rest/v1/service/TenantService.ts
import { Action, Entity } from '../../../../types/Authorization';
-import { CryptoSettingsType, KeySetting, SettingDB, SettingDBContent } from '../../../../types/Setting';
+import { CryptoKeySetting, CryptoSettingsType, SettingDB, SettingDBContent } from '../../../../types/Setting';
import { HTTPAuthError, HTTPError } from '../../../../types/HTTPError';
import { NextFunction, Request, Response } from 'express';
import Tenant, { TenantLogo } from '../../../../types/Tenant';
@@ -180,7 +180,7 @@ export default class TenantService {
key: Utils.generateKey(),
keyProperties: Utils.getDefaultKeyProperties()
}
- } as KeySetting;
+ } as CryptoKeySetting;
// Save Crypto Key Settings
await SettingStorage.saveCryptoSettings(tenantID, keySettingToSave);
}

MODIFY
src/storage/mongodb/SettingStorage.ts
src/storage/mongodb/SettingStorage.ts
-import { AnalyticsSettings, AnalyticsSettingsType, AssetSettings, AssetSettingsType, BillingSettings, BillingSettingsType, CryptoSetting, CryptoSettingsType, KeySetting, PricingSettings, PricingSettingsType, RefundSettings, RefundSettingsType, RoamingSettings, SettingDB, SmartChargingSettings, SmartChargingSettingsType } from '../../types/Setting';
+import { AnalyticsSettings, AnalyticsSettingsType, AssetSettings, AssetSettingsType, BillingSettings, BillingSettingsType, CryptoKeySetting, CryptoSetting, CryptoSettingsType, PricingSettings, PricingSettingsType, RefundSettings, RefundSettingsType, RoamingSettings, SettingDB, SmartChargingSettings, SmartChargingSettingsType } from '../../types/Setting';
import global, { FilterParams } from '../../types/GlobalType';
import BackendError from '../../exception/BackendError';
@@ -304,7 +304,7 @@ export default class SettingStorage {
}
}
- public static async saveCryptoSettings(tenantID: string, cryptoSettingToSave: KeySetting): Promise<void> {
+ public static async saveCryptoSettings(tenantID: string, cryptoSettingToSave: CryptoKeySetting): Promise<void> {
// Build internal structure
const settingsToSave = {
id: cryptoSettingToSave.id,
@@ -318,7 +318,7 @@ export default class SettingStorage {
await this.saveSettings(tenantID, settingsToSave);
}
- public static async getCryptoSettings(tenantID: string): Promise<KeySetting> {
+ public static async getCryptoSettings(tenantID: string): Promise<CryptoKeySetting> {
// Get the Crypto Key settings
const settings = await SettingStorage.getSettings(tenantID,
{ identifier: TenantComponents.CRYPTO },
@@ -343,7 +343,7 @@ export default class SettingStorage {
identifier: settings.result[0].identifier,
type: CryptoSettingsType.CRYPTO,
crypto: cryptoSetting
- } as KeySetting;
+ } as CryptoKeySetting;
return keySetting;
}
}

MODIFY
src/types/Setting.ts
src/types/Setting.ts
@@ -246,7 +246,7 @@ export enum CryptoSettingsType {
CRYPTO = 'crypto'
}
-export interface KeySetting extends Setting {
+export interface CryptoKeySetting extends Setting {
identifier: TenantComponents.CRYPTO;
type: CryptoSettingsType;
crypto: CryptoSetting;

MODIFY
src/utils/Cypher.ts
src/utils/Cypher.ts
@@ -11,7 +11,7 @@ const MODULE_NAME = 'Cypher';
export default class Cypher {
- public static async encrypt(data: string, tenantID: string): Promise<string> {
+ public static async encrypt(tenantID: string, data: string): Promise<string> {
const iv = crypto.randomBytes(IV_LENGTH);
const cryptoSetting = await Cypher.getCryptoSetting(tenantID);
const cipher = crypto.createCipheriv(Utils.buildAlgorithm(cryptoSetting.keyProperties), Buffer.from(cryptoSetting.key), iv);
@@ -20,7 +20,7 @@ export default class Cypher {
return iv.toString('hex') + ':' + encryptedData.toString('hex');
}
- public static async decrypt(data: string, tenantID: string): Promise<string> {
+ public static async decrypt(tenantID: string, data: string): Promise<string> {
const dataParts = data.split(':');
const iv = Buffer.from(dataParts.shift(), 'hex');
const encryptedData = Buffer.from(dataParts.join(':'), 'hex');
@@ -35,7 +35,7 @@ export default class Cypher {
return crypto.createHash('sha256').update(data).digest('hex');
}
- public static async encryptSensitiveDataInJSON(obj: Record<string, any>, tenantID: string): Promise<void> {
+ public static async encryptSensitiveDataInJSON(tenantID: string, obj: Record<string, any>): Promise<void> {
if (typeof obj !== 'object') {
throw new BackendError({
source: Constants.CENTRAL_SERVER,
@@ -60,7 +60,7 @@ export default class Cypher {
const value = _.get(obj, property);
// If the value is undefined, null or empty then do nothing and skip to the next property
if (value && value.length > 0) {
- _.set(obj, property, await Cypher.encrypt(value, tenantID));
+ _.set(obj, property, await Cypher.encrypt(tenantID, value));
}
}
}
@@ -69,37 +69,38 @@ export default class Cypher {
}
}
- public static async decryptSensitiveDataInJSON(obj: Record<string, any>, tenantID: string): Promise<void> {
- if (typeof obj !== 'object') {
- throw new BackendError({
- source: Constants.CENTRAL_SERVER,
- module: MODULE_NAME,
- method: 'decryptSensitiveDataInJSON',
- message: `The parameter ${obj} is not an object`
- });
- }
- if ('sensitiveData' in obj) {
- // Check that sensitive data is an array
- if (!Array.isArray(obj.sensitiveData)) {
- throw new BackendError({
- source: Constants.CENTRAL_SERVER,
- module: MODULE_NAME,
- method: 'decryptSensitiveDataInJSON',
- message: 'The property \'sensitiveData\' is not an array'
- });
- }
- for (const property of obj.sensitiveData as string[]) {
- // Check that the property does exist otherwise skip to the next property
- if (_.has(obj, property)) {
- const value = _.get(obj, property);
- // If the value is undefined, null or empty then do nothing and skip to the next property
- if (value && value.length > 0) {
- _.set(obj, property, await Cypher.decrypt(value, tenantID));
- }
- }
- }
- }
- }
+ // Not used!!!
+ // public static async decryptSensitiveDataInJSON(tenantID: string, obj: Record<string, any>): Promise<void> {
+ // if (typeof obj !== 'object') {
+ // throw new BackendError({
+ // source: Constants.CENTRAL_SERVER,
+ // module: MODULE_NAME,
+ // method: 'decryptSensitiveDataInJSON',
+ // message: `The parameter ${obj} is not an object`
+ // });
+ // }
+ // if ('sensitiveData' in obj) {
+ // // Check that sensitive data is an array
+ // if (!Array.isArray(obj.sensitiveData)) {
+ // throw new BackendError({
+ // source: Constants.CENTRAL_SERVER,
+ // module: MODULE_NAME,
+ // method: 'decryptSensitiveDataInJSON',
+ // message: 'The property \'sensitiveData\' is not an array'
+ // });
+ // }
+ // for (const property of obj.sensitiveData as string[]) {
+ // // Check that the property does exist otherwise skip to the next property
+ // if (_.has(obj, property)) {
+ // const value = _.get(obj, property);
+ // // If the value is undefined, null or empty then do nothing and skip to the next property
+ // if (value && value.length > 0) {
+ // _.set(obj, property, await Cypher.decrypt(tenantID, value));
+ // }
+ // }
+ // }
+ // }
+ // }
public static hashSensitiveDataInJSON(obj: Record<string, any>): void {
if (typeof obj !== 'object') {
@@ -134,15 +135,15 @@ export default class Cypher {
}
private static async getCryptoSetting(tenantID: string): Promise<CryptoSetting> {
- const cryptoSettings = (await SettingStorage.getCryptoSettings(tenantID)).crypto;
- if (!cryptoSettings) {
+ const cryptoSettings = await SettingStorage.getCryptoSettings(tenantID);
+ if (!cryptoSettings || !cryptoSettings.crypto) {
throw new BackendError({
source: Constants.CENTRAL_SERVER,
module: MODULE_NAME,
method: 'getCryptoSetting',
- message: `Tenant with ID: ${tenantID}) does not have crypto settings.`
+ message: `Tenant ID '${tenantID}' does not have crypto settings.`
});
}
- return cryptoSettings;
+ return cryptoSettings.crypto;
}
}

MODIFY
test/api/BillingTest.ts
test/api/BillingTest.ts
@@ -45,14 +45,14 @@ class TestData {
public static async setBillingSystemValidCredentials(testData) {
const stripeSettings = TestData.getStripeSettings();
await TestData.saveBillingSettings(testData, stripeSettings);
- stripeSettings.secretKey = await Cypher.encrypt(stripeSettings.secretKey, testData.tenantContext.getTenant().id);
+ stripeSettings.secretKey = await Cypher.encrypt(testData.tenantContext.getTenant().id, stripeSettings.secretKey);
billingImpl = new StripeBillingIntegration(testData.tenantContext.getTenant().id, stripeSettings);
expect(billingImpl).to.not.be.null;
}
public static async setBillingSystemInvalidCredentials(testData) {
const stripeSettings = TestData.getStripeSettings();
- stripeSettings.secretKey = await Cypher.encrypt('sk_test_invalid_credentials', testData.tenantContext.getTenant().id);
+ stripeSettings.secretKey = await Cypher.encrypt(testData.tenantContext.getTenant().id, 'sk_test_invalid_credentials');
await TestData.saveBillingSettings(testData, stripeSettings);
billingImpl = new StripeBillingIntegration(testData.tenantContext.getTenant().id, stripeSettings);
expect(billingImpl).to.not.be.null;

MODIFY
test/api/SmartChargingTest.ts
test/api/SmartChargingTest.ts
@@ -48,7 +48,7 @@ class TestData {
public static async setSmartChargingValidCredentials(testData): Promise<void> {
const sapSmartChargingSettings = TestData.getSmartChargingSettings();
await TestData.saveSmartChargingSettings(testData, sapSmartChargingSettings);
- sapSmartChargingSettings.password = await Cypher.encrypt(sapSmartChargingSettings.password, testData.tenantContext.getTenant().id);
+ sapSmartChargingSettings.password = await Cypher.encrypt(testData.tenantContext.getTenant().id, sapSmartChargingSettings.password);
aCBufferFactor = 1 + sapSmartChargingSettings.limitBufferAC / 100,
dCBufferFactor = 1 + sapSmartChargingSettings.limitBufferDC / 100,
smartChargingIntegration = await SmartChargingFactory.getSmartChargingImpl(testData.tenantContext.getTenant().id);

MODIFY
test/api/context/BillingContext.ts
test/api/context/BillingContext.ts
@@ -85,7 +85,7 @@ export default class BillingContext {
const tenantBillingSettings = await SettingStorage.getBillingSettings(this.tenantContext.getTenant().id);
tenantBillingSettings.stripe = stripeSettings;
tenantBillingSettings.sensitiveData = ['content.stripe.secretKey'];
- tenantBillingSettings.stripe.secretKey = await Cypher.encrypt(stripeSettings.secretKey, this.tenantContext.getTenant().id);
+ tenantBillingSettings.stripe.secretKey = await Cypher.encrypt(this.tenantContext.getTenant().id, stripeSettings.secretKey);
await SettingStorage.saveBillingSettings(this.tenantContext.getTenant().id, tenantBillingSettings);
}
}

Set tenantID param at first position in Cypher class + Fix sub-modules setting access