begin of validators schemas
ADD
src/assets/server/rest/v1/schemas/setting/setting-ocpi-set.json
+{
+ "$id": "setting-ocpi-set",
+ "title": "Set OCPI Settings",
+ "type": "object",
+ "properties": {
+ "content": {
+ "type": "object",
+ "properties": {
+ "ocpi": {
+ "type": "object",
+ "properties": {
+ "businessDetails": {
+ "type": "object",
+ "properties": {
+ "logo": {
+ "type": "object",
+ "properties": {
+ "category": {
+ "type": "string",
+ "sanitize" : "mongo"
+ },
+ "height": {
+ "type": "number",
+ "sanitize": "mongo",
+ "nullable": true
+ },
+ "thumbnail": {
+ "type": "string",
+ "sanitize": "mongo"
+ },
+ "type": {
+ "type": "string",
+ "sanitize": "mongo"
+ },
+ "url": {
+ "type": "string",
+ "sanitize": "mongo"
+ },
+ "width": {
+ "type": "number",
+ "sanitize": "mongo",
+ "nullable": true
+ }
+ }
+ },
+ "name": {
+ "type": "string",
+ "sanitize": "mongo"
+ },
+ "website": {
+ "type": "string",
+ "sanitize": "mongo"
+ }
+ }
+ },
+ "cpo": {
+ "type": "object",
+ "properties": {
+ "countryCode": {
+ "type": "string",
+ "sanitize": "mongo"
+ },
+ "partyID": {
+ "type": "string",
+ "sanitize": "mongo"
+ }
+ }
+ },
+ "currency": {
+ "type": "string",
+ "sanitize": "mongo"
+ },
+ "emsp": {
+ "type": "object",
+ "properties": {
+ "countryCode": {
+ "type": "string",
+ "sanitize": "mongo"
+ },
+ "partyID": {
+ "type": "string",
+ "sanitize": "mongo"
+ }
+ }
+ },
+ "tariffID": {
+ "type": "string",
+ "sanitize": "mongo"
+ }
+ }
+ },
+ "type": {
+ "type": "string",
+ "sanitize": "mongo",
+ "enum": ["ocpi"]
+ }
+ },
+ "required": [
+ "type",
+ "ocpi"
+ ]
+ },
+ "id": {
+ "type": "string",
+ "sanitize": "mongo"
+ },
+ "identifier": {
+ "type": "string",
+ "sanitize": "mongo",
+ "enum": ["ocpi"]
+ },
+ "sensitiveData": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "sanitize": "mongo"
+ }
+ }
+ },
+ "required": [
+ "id",
+ "identifier",
+ "content"
+ ]
+}

ADD
src/assets/server/rest/v1/schemas/setting/setting-smart-charging-set.json
+{
+ "$id": "setting-smart-charging-set",
+ "title": "Set Smart Charging Settings",
+ "type": "object",
+ "properties": {
+ "content": {
+ "type": "object",
+ "properties": {
+ "sapSmartCharging": {
+ "type": "object",
+ "properties": {
+ "limitBufferAC" : {
+ "type": "number",
+ "sanitize": "mongo"
+ },
+ "limitBufferDC": {
+ "type": "number",
+ "sanitize": "mongo"
+ },
+ "optimizerUrl": {
+ "type": "string",
+ "sanitize": "mongo"
+ },
+ "password": {
+ "type": "string",
+ "sanitize": "mongo"
+ },
+ "stickyLimitation": {
+ "type": "boolean",
+ "sanitize": "mongo"
+ },
+ "user": {
+ "type": "string",
+ "sanitize": "mongo"
+ }
+ }
+ },
+ "type": {
+ "type": "string",
+ "sanitize": "mongo",
+ "enum": ["sapSmartCharging"]
+ }
+ },
+ "required": [
+ "type",
+ "sapSmartCharging"
+ ]
+ },
+ "id": {
+ "type": "string",
+ "sanitize": "mongo"
+ },
+ "identifier": {
+ "type": "string",
+ "sanitize": "mongo",
+ "enum": ["smartCharging"]
+ },
+ "sensitiveData": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "sanitize": "mongo"
+ }
+ }
+ },
+ "required": [
+ "id",
+ "identifier",
+ "content"
+ ]
+}

ADD
src/assets/server/rest/v1/schemas/setting/setting-user-set.json
+{
+ "$id": "setting-user-set",
+ "title": "Set User Settings",
+ "type": "object",
+ "properties": {
+ "content": {
+ "type": "object",
+ "properties": {
+ "user": {
+ "type": "object",
+ "properties": {
+ "autoActivateAccountAfterValidation" : {
+ "type": "boolean",
+ "sanitize": "mongo"
+ }
+ }
+ },
+ "type": {
+ "type": "string",
+ "sanitize": "mongo",
+ "enum": ["user"]
+ }
+ },
+ "required": [
+ "type",
+ "user"
+ ]
+ },
+ "id": {
+ "type": "string",
+ "sanitize": "mongo"
+ },
+ "identifier": {
+ "type": "string",
+ "sanitize": "mongo",
+ "enum": ["user"]
+ },
+ "sensitiveData": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "sanitize": "mongo"
+ }
+ }
+ },
+ "required": [
+ "id",
+ "identifier",
+ "content"
+ ]
+}

MODIFY
src/server/rest/v1/service/SettingService.ts
src/server/rest/v1/service/SettingService.ts
@@ -11,6 +11,7 @@ import Logging from '../../../../utils/Logging';
import { ServerAction } from '../../../../types/Server';
import SettingSecurity from './security/SettingSecurity';
import SettingStorage from '../../../../storage/mongodb/SettingStorage';
+import SettingValidator from '../validator/SettingValidator';
import { StatusCodes } from 'http-status-codes';
import { TechnicalSettings } from '../../../../types/Setting';
import Utils from '../../../../utils/Utils';
@@ -176,9 +177,34 @@ export default class SettingService {
}
public static async handleUpdateSetting(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ let filteredRequest: any;
+ UtilsService.assertIdIsProvided(action, req.body.id, MODULE_NAME, 'handleUpdateSetting', req.user);
+ switch (req.body.identifier) {
// Filter
+ case TechnicalSettings.OCPI:
+ filteredRequest = SettingValidator.getInstance().validateSettingOCPISetReq(req.body);
+ break;
+ case TechnicalSettings.CRYPTO:
+ // filteredRequest = SettingValidator.getInstance().validateSettingCryptoSetReq(req.body);
+ break;
+ case TechnicalSettings.USER:
+ filteredRequest = SettingValidator.getInstance().validateSettingUserSetReq(req.body);
+ break;
+ case TechnicalSettings.SMART_CHARGING:
+ filteredRequest = SettingValidator.getInstance().validateSettingSmartChargingSetReq(req.body);
+ break;
+ default:
+ throw new AppError({
+ source: Constants.CENTRAL_SERVER,
+ errorCode: HTTPError.NOT_IMPLEMENTED_ERROR,
+ message: `The property 'identifier' with value '${req.body.identifier as string}' for Setting with ID '${req.body.id as string}' is not implemented`,
+ module: MODULE_NAME,
+ method: 'handleUpdateSetting',
+ user: req.user
+ });
+ }
+ // TODO : Check for each Setting Type that we do have equivalent results
const settingUpdate = SettingSecurity.filterSettingUpdateRequest(req.body);
- UtilsService.assertIdIsProvided(action, settingUpdate.id, MODULE_NAME, 'handleUpdateSetting', req.user);
// Check auth
if (!await Authorizations.canUpdateSetting(req.user)) {
throw new AppAuthError({
@@ -186,30 +212,30 @@ export default class SettingService {
user: req.user,
action: Action.UPDATE, entity: Entity.SETTING,
module: MODULE_NAME, method: 'handleUpdateSetting',
- value: settingUpdate.id
+ value: filteredRequest.id
});
}
// Get Setting
- const setting = await SettingStorage.getSetting(req.tenant, settingUpdate.id);
- UtilsService.assertObjectExists(action, setting, `Setting ID '${settingUpdate.id}' does not exist`,
+ const setting = await SettingStorage.getSetting(req.tenant, filteredRequest.id);
+ UtilsService.assertObjectExists(action, setting, `Setting ID '${filteredRequest.id as string}' does not exist`,
MODULE_NAME, 'handleUpdateSetting', req.user);
// Process the sensitive data if any
// Preprocess the data to take care of updated values
- if (settingUpdate.sensitiveData) {
- if (!Array.isArray(settingUpdate.sensitiveData)) {
+ if (filteredRequest.sensitiveData) {
+ if (!Array.isArray(filteredRequest.sensitiveData)) {
throw new AppError({
source: Constants.CENTRAL_SERVER,
errorCode: HTTPError.CYPHER_INVALID_SENSITIVE_DATA_ERROR,
- message: `The property 'sensitiveData' for Setting with ID '${settingUpdate.id}' is not an array`,
+ message: `The property 'sensitiveData' for Setting with ID '${filteredRequest.id as string}' is not an array`,
module: MODULE_NAME,
method: 'handleUpdateSetting',
user: req.user
});
}
// Process sensitive properties
- for (const property of settingUpdate.sensitiveData) {
+ for (const property of filteredRequest.sensitiveData) {
// Get the sensitive property from the request
- const valueInRequest = _.get(settingUpdate, property);
+ const valueInRequest = _.get(filteredRequest, property);
if (valueInRequest && valueInRequest.length > 0) {
// Get the sensitive property from the DB
const valueInDb = _.get(setting, property);
@@ -217,27 +243,27 @@ export default class SettingService {
const hashedValueInDB = Cypher.hash(valueInDb);
if (valueInRequest !== hashedValueInDB) {
// Yes: Encrypt
- _.set(settingUpdate, property, await Cypher.encrypt(req.tenant, valueInRequest));
+ _.set(filteredRequest, property, await Cypher.encrypt(req.tenant, valueInRequest));
} else {
// No: Put back the encrypted value
- _.set(settingUpdate, property, valueInDb);
+ _.set(filteredRequest, property, valueInDb);
}
} else {
// Value in db is empty then encrypt
- _.set(settingUpdate, property, await Cypher.encrypt(req.tenant, valueInRequest));
+ _.set(filteredRequest, property, await Cypher.encrypt(req.tenant, valueInRequest));
}
}
}
} else {
- settingUpdate.sensitiveData = [];
+ filteredRequest.sensitiveData = [];
}
// Update timestamp
setting.lastChangedBy = { 'id': req.user.id };
setting.lastChangedOn = new Date();
- if (settingUpdate.identifier === TechnicalSettings.CRYPTO) {
+ if (filteredRequest.identifier === TechnicalSettings.CRYPTO) {
// Check supported algorithm
if (!Constants.CRYPTO_SUPPORTED_ALGORITHM.includes(
- Utils.buildCryptoAlgorithm(settingUpdate.content.crypto.keyProperties))) {
+ Utils.buildCryptoAlgorithm(filteredRequest.content.crypto.keyProperties))) {
throw new AppError({
source: Constants.CENTRAL_SERVER,
errorCode: HTTPError.CRYPTO_ALGORITHM_NOT_SUPPORTED,
@@ -247,8 +273,8 @@ export default class SettingService {
});
}
// Check crypto key
- const keyLength = settingUpdate.content.crypto.keyProperties.blockSize / 8;
- if (settingUpdate.content.crypto.key.length !== keyLength) {
+ const keyLength = filteredRequest.content.crypto.keyProperties.blockSize / 8;
+ if (filteredRequest.content.crypto.key.length !== keyLength) {
throw new AppError({
source: Constants.CENTRAL_SERVER,
errorCode: HTTPError.CRYPTO_KEY_LENGTH_INVALID,
@@ -259,7 +285,7 @@ export default class SettingService {
}
// Check if config is valid
try {
- await Cypher.checkCryptoSettings(settingUpdate.content.crypto);
+ await Cypher.checkCryptoSettings(filteredRequest.content.crypto);
} catch (error) {
throw new AppError({
source: Constants.CENTRAL_SERVER,
@@ -279,18 +305,18 @@ export default class SettingService {
user: req.user
});
} else {
- if (Cypher.hash(settingUpdate.content.crypto.key) !== Cypher.hash(setting.content.crypto.key)) {
- settingUpdate.content.crypto.migrationToBeDone = true;
+ if (Cypher.hash(filteredRequest.content.crypto.key) !== Cypher.hash(setting.content.crypto.key)) {
+ filteredRequest.content.crypto.migrationToBeDone = true;
}
- settingUpdate.content.crypto.formerKey = setting.content.crypto.key;
- settingUpdate.content.crypto.formerKeyProperties = setting.content.crypto.keyProperties;
+ filteredRequest.content.crypto.formerKey = setting.content.crypto.key;
+ filteredRequest.content.crypto.formerKeyProperties = setting.content.crypto.keyProperties;
}
}
// Update Setting
- settingUpdate.id = await SettingStorage.saveSettings(req.tenant, settingUpdate);
+ filteredRequest.id = await SettingStorage.saveSettings(req.tenant, filteredRequest);
// Crypto Setting handling
- if (settingUpdate.identifier === TechnicalSettings.CRYPTO) {
- if (settingUpdate.content.crypto.migrationToBeDone) {
+ if (filteredRequest.identifier === TechnicalSettings.CRYPTO) {
+ if (filteredRequest.content.crypto.migrationToBeDone) {
await Cypher.handleCryptoSettingsChange(req.tenant);
}
}
@@ -298,9 +324,9 @@ export default class SettingService {
await Logging.logSecurityInfo({
tenantID: req.user.tenantID,
user: req.user, module: MODULE_NAME, method: 'handleUpdateSetting',
- message: `Setting '${settingUpdate.id}' has been updated successfully`,
+ message: `Setting '${filteredRequest.id as string}' has been updated successfully`,
action: action,
- detailedMessages: { settingUpdate }
+ detailedMessages: { filteredRequest }
});
// Ok
res.json(Constants.REST_RESPONSE_SUCCESS);

ADD
src/server/rest/v1/validator/SettingValidator.ts
+import { HttpSettingOCPISetRequest, HttpSettingSmartChargingSetRequest, HttpSettingUserSetRequest } from '../../../../types/requests/HttpSettingRequest';
+
+import Schema from '../../../../types/validator/Schema';
+import SchemaValidator from '../../../../validator/SchemaValidator';
+import fs from 'fs';
+import global from '../../../../types/GlobalType';
+
+export default class SettingValidator extends SchemaValidator {
+ private static instance: SettingValidator|null = null;
+ private settingOCPISet: Schema;
+ private settingUserSet: Schema;
+ private settingSmartChargingSet: Schema;
+
+ private constructor() {
+ super('SettingValidator');
+ this.settingOCPISet = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/setting/setting-ocpi-set.json`, 'utf8'));
+ this.settingSmartChargingSet = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/setting/setting-smart-charging-set.json`, 'utf8'));
+ this.settingUserSet = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/setting/setting-user-set.json`, 'utf8'));
+ }
+
+ public static getInstance(): SettingValidator {
+ if (!SettingValidator.instance) {
+ SettingValidator.instance = new SettingValidator();
+ }
+ return SettingValidator.instance;
+ }
+
+ public validateSettingOCPISetReq(data: Record<string, unknown>): HttpSettingOCPISetRequest {
+ return this.validate(this.settingOCPISet, data);
+ }
+
+ public validateSettingUserSetReq(data: Record<string, unknown>): HttpSettingUserSetRequest {
+ return this.validate(this.settingUserSet, data);
+ }
+
+ public validateSettingSmartChargingSetReq(data: Record<string, unknown>): HttpSettingSmartChargingSetRequest {
+ return this.validate(this.settingUserSet, data);
+ }
+}

MODIFY
src/types/Setting.ts
src/types/Setting.ts
@@ -3,7 +3,9 @@ import { TenantComponents } from './Tenant';
export enum TechnicalSettings {
USER = 'user',
- CRYPTO = 'crypto'
+ CRYPTO = 'crypto',
+ OCPI = 'ocpi',
+ SMART_CHARGING = 'smartCharging'
}
export interface Setting extends CreatedUpdatedProps {

MODIFY
src/types/requests/HttpSettingRequest.ts
src/types/requests/HttpSettingRequest.ts
@@ -9,3 +9,62 @@ export interface HttpSettingsRequest extends HttpDatabaseRequest {
Identifier?: string;
ContentFilter?: boolean;
}
+
+export interface HttpSettingSetRequest {
+ id: string,
+ identifier: string,
+ sensitiveData: []
+}
+
+export interface HttpSettingOCPISetRequest extends HttpSettingSetRequest {
+ content: {
+ type: string,
+ ocpi: {
+ businessDetails: {
+ logo: {
+ category?: string,
+ height?: number,
+ thumbnail?: string,
+ type?: string,
+ url?: string,
+ width?: number
+ },
+ name: string,
+ website: string
+ },
+ cpo: {
+ countryCode: string,
+ partyID: string
+ },
+ currency: string,
+ emsp: {
+ countryCode: string,
+ partyID: string
+ },
+ tariffID?: string
+ }
+ }
+}
+
+export interface HttpSettingUserSetRequest extends HttpSettingSetRequest {
+ content: {
+ type: string,
+ user: {
+ autoActivateAccountAfterValidation: boolean
+ }
+ }
+}
+
+export interface HttpSettingSmartChargingSetRequest extends HttpSettingSetRequest {
+ content: {
+ type: string,
+ sapSmartCharging: {
+ limitBufferAC: number,
+ limitBufferDC: number,
+ optimizerUrl: string,
+ password: string,
+ stickyLimitation: boolean,
+ user: string
+ }
+ }
+}

begin of validators schemas