Added CS action schema validation
ADD
src/assets/server/rest/v1/schemas/chargingstation/chargingstation-action.json
+{
+ "title": "Command Charging Station Request",
+ "type": "object",
+ "properties": {
+ "chargeBoxID": {
+ "$ref": "chargingstation.json#/definitions/id"
+ },
+ "carID": {
+ "$ref": "common.json#/definitions/id"
+ },
+ "args": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "sanitize": "mongo"
+ },
+ "key": {
+ "type": ["string", "array"],
+ "sanitize": "mongo"
+ },
+ "value": {
+ "type": "string",
+ "sanitize": "mongo"
+ },
+ "custom": {
+ "type": "boolean",
+ "sanitize": "mongo"
+ },
+ "connectorId": {
+ "$ref": "chargingstation.json#/definitions/connectorID"
+ },
+ "duration": {
+ "type": "number",
+ "sanitize": "mongo"
+ },
+ "chargingRateUnit": {
+ "type": "string",
+ "sanitize": "mongo",
+ "enum": ["A", "W"]
+ },
+ "chargingProfilePurpose": {
+ "$ref": "chargingstation.json#/definitions/chargingProfilePurpose"
+ },
+ "stackLevel": {
+ "$ref": "chargingstation.json#/definitions/stackLevel"
+ },
+ "tagID": {
+ "$ref": "tag.json#/definitions/id"
+ },
+ "location": {
+ "type": "string",
+ "sanitize": "mongo"
+ },
+ "retries": {
+ "type": "number",
+ "sanitize": "mongo"
+ },
+ "retryInterval": {
+ "type": "number",
+ "sanitize": "mongo"
+ },
+ "startTime": {
+ "type": "string",
+ "format": "date-time",
+ "sanitize": "mongo"
+ },
+ "stopTime": {
+ "type": "string",
+ "format": "date-time",
+ "sanitize": "mongo"
+ },
+ "retrieveDate": {
+ "type": "string",
+ "format": "date-time",
+ "sanitize": "mongo"
+ },
+ "transactionId": {
+ "$ref": "transaction.json#/definitions/id"
+ },
+ "csChargingProfile": {
+ "$ref": "chargingstation.json#/definitions/chargingProfile"
+ }
+ }
+ },
+ "chargePointID": {
+ "$ref": "common.json#/definitions/id"
+ },
+ "profile": {
+ "$ref": "chargingstation.json#/definitions/chargingProfile"
+ }
+ },
+ "required": [
+ "chargeBoxID"
+ ]
+}

MODIFY
src/assets/server/rest/v1/schemas/chargingstation/chargingstation-delete.json
src/assets/server/rest/v1/schemas/chargingstation/chargingstation-delete.json
"type": "object",
"properties": {
"id": {
- "$ref": "common.json#/definitions/id"
+ "$ref": "chargingstation.json#/definitions/id"
}
},
"required": [

MODIFY
src/assets/server/rest/v1/schemas/chargingstation/chargingstation-get.json
src/assets/server/rest/v1/schemas/chargingstation/chargingstation-get.json
"type": "object",
"properties": {
"id": {
- "$ref": "common.json#/definitions/id"
+ "$ref": "chargingstation.json#/definitions/id"
},
"ProjectFields": {
"$ref": "common.json#/definitions/projectFields"

ADD
src/assets/server/rest/v1/schemas/chargingstation/chargingstation.json
+{
+ "$id": "chargingstation.json",
+ "definitions": {
+ "id": {
+ "type": "string",
+ "sanitize": "mongo"
+ },
+ "chargingProfile": {
+ "type": "object",
+ "properties": {
+ "chargingProfileId": {
+ "$ref": "common.json#/definitions/id"
+ },
+ "transactionId": {
+ "$ref": "common.json#/definitions/id"
+ },
+ "stackLevel": {
+ "$ref": "#/definitions/stackLevel"
+ },
+ "chargingProfilePurpose": {
+ "$ref": "#/definitions/chargingProfilePurpose"
+ },
+ "chargingProfileKind": {
+ "type": "string",
+ "sanitize": "mongo"
+ },
+ "recurrencyKind": {
+ "type": "string",
+ "sanitize": "mongo"
+ },
+ "validFrom": {
+ "type": "string",
+ "format": "date",
+ "sanitize": "mongo"
+ },
+ "validTo": {
+ "type": "string",
+ "format": "date",
+ "sanitize": "mongo"
+ },
+ "chargingSchedule": {
+ "$ref": "#/definitions/chargingSchedule"
+ }
+ }
+ },
+ "chargingSchedule": {
+ "type": "object",
+ "properties": {
+ "duration": {
+ "type": "number",
+ "sanitize": "mongo"
+ },
+ "startSchedule": {
+ "type": "string",
+ "format": "date",
+ "sanitize": "mongo"
+ },
+ "chargingRateUnit": {
+ "type": "string",
+ "sanitize": "mongo"
+ },
+ "chargingSchedulePeriod": {
+ "$ref": "#/definitions/chargingSchedulePeriod"
+ },
+ "minChargeRate": {
+ "type": "number",
+ "sanitize": "mongo"
+ }
+ },
+ "required": ["chargingRateUnit", "chargingSchedulePeriod"]
+ },
+ "chargingSchedulePeriod": {
+ "type": "object",
+ "properties": {
+ "startPeriod": {
+ "type": "number",
+ "sanitize": "mongo"
+ },
+ "limit": {
+ "type": "number",
+ "sanitize": "mongo"
+ },
+ "numberPhases": {
+ "type": "number",
+ "sanitize": "mongo"
+ }
+ },
+ "required": ["startPeriod", "limit"]
+ },
+ "connectorID": {
+ "type": "number",
+ "sanitize": "mongo"
+ },
+ "chargingProfilePurpose": {
+ "type": "string",
+ "sanitize": "mongo"
+ },
+ "stackLevel": {
+ "type": "number",
+ "sanitize": "mongo"
+ }
+ }
+}

MODIFY
src/assets/server/rest/v1/schemas/chargingstation/chargingstations-get.json
src/assets/server/rest/v1/schemas/chargingstation/chargingstations-get.json
"sanitize": "mongo"
},
"SiteID": {
- "$ref": "common.json#/definitions/id"
+ "$ref": "common.json#/definitions/ids"
},
"WithSite": {
"type": "boolean",
"sanitize": "mongo"
},
"SiteAreaID": {
- "$ref": "common.json#/definitions/id"
+ "$ref": "common.json#/definitions/ids"
},
"ConnectorStatus": {
"type": "string",
"sanitize": "mongo"
},
"ChargeBoxID": {
- "type": "string",
- "sanitize": "mongo"
+ "$ref": "chargingstation.json#/definitions/id"
},
"IncludeDeleted": {
"type": "boolean",

MODIFY
src/assets/server/rest/v1/schemas/common/common.json
src/assets/server/rest/v1/schemas/common/common.json
"pattern": "^$|^[0-9a-fA-F]{24}$",
"sanitize": "mongo"
},
+ "ids": {
+ "type": "string",
+ "pattern": "^$|^([0-9a-fA-F]{24}\\|?)*$",
+ "sanitize": "mongo"
+ },
"name": {
"type": "string",
"minLength": 1,

ADD
src/assets/server/rest/v1/schemas/tag/tag.json
+{
+ "$id": "tag.json",
+ "definitions": {
+ "id": {
+ "type": "string",
+ "pattern": "^[\\w\\d]*$",
+ "sanitize": "mongo"
+ }
+ }
+}

ADD
src/assets/server/rest/v1/schemas/transaction/transaction.json
+{
+ "$id": "transaction.json",
+ "definitions": {
+ "id": {
+ "type": "number",
+ "sanitize": "mongo"
+ }
+ }
+}

MODIFY
src/server/rest/v1/router/api/ChargingStationRouter.ts
src/server/rest/v1/router/api/ChargingStationRouter.ts
@@ -55,14 +55,12 @@ export default class ChargingStationRouter {
protected buildRouteChargingStation(): void {
this.router.get(`/${ServerRoute.REST_CHARGING_STATION}`, async (req: Request, res: Response, next: NextFunction) => {
- req.query.ID = req.params.id;
await RouterUtils.handleServerAction(ChargingStationService.handleGetChargingStation.bind(this), ServerAction.CHARGING_STATION, req, res, next);
});
}
protected buildRouteChargingStationDelete(): void {
this.router.delete(`/${ServerRoute.REST_CHARGING_STATIONS}/:id`, async (req: Request, res: Response, next: NextFunction) => {
- req.query.ID = req.params.id;
await RouterUtils.handleServerAction(ChargingStationService.handleDeleteChargingStation.bind(this), ServerAction.CHARGING_STATION_DELETE, req, res, next);
});
}

MODIFY
src/server/rest/v1/service/ChargingStationService.ts
src/server/rest/v1/service/ChargingStationService.ts
@@ -695,7 +695,7 @@ export default class ChargingStationService {
public static async handleDeleteChargingStation(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
// Filter
- const chargingStationID = ChargingStationValidator.getInstance().validateChargingStationDeleteReq(req.query).id;
+ const chargingStationID = ChargingStationValidator.getInstance().validateChargingStationDeleteReq(req.params).id;
// Check Mandatory fields
UtilsService.assertIdIsProvided(action, chargingStationID, MODULE_NAME,
'handleDeleteChargingStation', req.user);
@@ -818,7 +818,7 @@ export default class ChargingStationService {
public static async handleGetChargingStation(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
// Filter
- const filteredRequest = ChargingStationValidator.getInstance().validateChargingStationGetReq(req.query);
+ const filteredRequest = ChargingStationValidator.getInstance().validateChargingStationGetReq({ ...req.params, ...req.query });
UtilsService.assertIdIsProvided(action, filteredRequest.id, MODULE_NAME, 'handleGetChargingStation', req.user);
// Check auth
if (!Authorizations.canReadChargingStation(req.user)) {
@@ -839,8 +839,9 @@ export default class ChargingStationService {
'createdOn', 'chargeBoxSerialNumber', 'chargePointSerialNumber', 'powerLimitUnit'
];
// Check projection
- if (!Utils.isEmptyArray(filteredRequest.ProjectFields)) {
- projectFields = projectFields.filter((projectField) => filteredRequest.ProjectFields.includes(projectField));
+ const httpProjectFields = UtilsService.httpFilterProjectToMongoDB(filteredRequest.ProjectFields);
+ if (!Utils.isEmptyArray(httpProjectFields)) {
+ projectFields = projectFields.filter((projectField) => httpProjectFields.includes(projectField));
}
// Query charging station
const chargingStation = await ChargingStationStorage.getChargingStation(
@@ -1081,7 +1082,7 @@ export default class ChargingStationService {
public static async handleAction(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
// Filter - Type is hacked because code below is. Would need approval to change code structure.
const command = action.slice(15) as Command;
- const filteredRequest = ChargingStationSecurity.filterChargingStationActionRequest(req.body);
+ const filteredRequest = ChargingStationValidator.getInstance().validateChargingStationActionReq(req.body);
UtilsService.assertIdIsProvided(action, filteredRequest.chargeBoxID, MODULE_NAME, 'handleAction', req.user);
// Get the Charging station
const chargingStation = await ChargingStationStorage.getChargingStation(req.user.tenantID, filteredRequest.chargeBoxID);
@@ -1342,7 +1343,13 @@ export default class ChargingStationService {
});
}
// Filter
- const filteredRequest = ChargingStationValidator.getInstance().validateChargingStationsGetReq(req.query);
+ let filteredRequest;
+ try {
+ filteredRequest = ChargingStationValidator.getInstance().validateChargingStationsGetReq(req.query);
+ } catch (e) {
+ const a = 5;
+ }
+
// Check Users
let userProject: string[] = [];
if (Authorizations.canListUsers(req.user)) {

MODIFY
src/server/rest/v1/validator/ChargingStationValidator.ts
src/server/rest/v1/validator/ChargingStationValidator.ts
-import HttpByIDRequest, { HttpRestByIDRequest } from '../../../../types/requests/HttpByIDRequest';
+import { HttpChargingStationCommandRequest, HttpChargingStationsRequest } from '../../../../types/requests/HttpChargingStationRequest';
-import { HttpChargingStationsRequest } from '../../../../types/requests/HttpChargingStationRequest';
+import { HttpRestByIDRequest } from '../../../../types/requests/HttpByIDRequest';
import Schema from './Schema';
import SchemaValidator from './SchemaValidator';
import fs from 'fs';
@@ -11,6 +11,7 @@ export default class ChargingStationValidator extends SchemaValidator {
private chargingStationsGet: Schema;
private chargingStationGet: Schema;
private chargingStationDelete: Schema;
+ private chargingStationAction: Schema;
private constructor() {
@@ -18,6 +19,7 @@ export default class ChargingStationValidator extends SchemaValidator {
this.chargingStationsGet = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/chargingstation/chargingstations-get.json`, 'utf8'));
this.chargingStationGet = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/chargingstation/chargingstation-get.json`, 'utf8'));
this.chargingStationDelete = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/chargingstation/chargingstation-delete.json`, 'utf8'));
+ this.chargingStationAction = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/chargingstation/chargingstation-action.json`, 'utf8'));
}
public static getInstance(): ChargingStationValidator {
@@ -44,4 +46,10 @@ export default class ChargingStationValidator extends SchemaValidator {
this.validate(this.chargingStationDelete, data);
return data;
}
+
+ public validateChargingStationActionReq(data: any): HttpChargingStationCommandRequest {
+ // Validate schema
+ this.validate(this.chargingStationAction, data);
+ return data;
+ }
}

MODIFY
src/server/rest/v1/validator/SchemaValidator.ts
src/server/rest/v1/validator/SchemaValidator.ts
@@ -15,6 +15,9 @@ export default class SchemaValidator {
private readonly ajv: Ajv.Ajv;
private commonSchema: any = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/common/common.json`, 'utf8'));
private tenantComponentSchema: any = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/tenant/tenant-components.json`, 'utf8'));
+ private chargingStationSchema: any = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/chargingstation/chargingstation.json`, 'utf8'));
+ private tagSchema: any = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/tag/tag.json`, 'utf8'));
+ private transactionSchema: any = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/transaction/transaction.json`, 'utf8'));
constructor(readonly moduleName: string,
config: {
@@ -29,6 +32,9 @@ export default class SchemaValidator {
this.ajv = ajvSanitizer(new Ajv(config), extraSanitizers);
this.ajv.addSchema(this.commonSchema);
this.ajv.addSchema(this.tenantComponentSchema);
+ this.ajv.addSchema(this.chargingStationSchema);
+ this.ajv.addSchema(this.tagSchema);
+ this.ajv.addSchema(this.transactionSchema);
}
public validate(schema: boolean | Record<string, unknown>, content: any): void {

Added CS action schema validation