Align Car service with ABAC auth
Use AuthService instead of Authorizations class
Small clean + alignement
Added loghelper for car
MODIFY
src/server/rest/v1/service/AuthorizationService.ts
src/server/rest/v1/service/AuthorizationService.ts
@@ -612,8 +612,8 @@ export default class AuthorizationService {
Utils.removeCanPropertiesWithFalseValue(car);
}
- public static async checkAndGetCarCatalogsAuthorizations(tenant: Tenant, userToken: UserToken,
- filteredRequest: Partial<HttpCarCatalogsRequest>): Promise<AuthorizationFilter> {
+ public static async checkAndGetCarCatalogsAuthorizations(tenant: Tenant, userToken: UserToken, authAction: Action,
+ filteredRequest?: Partial<HttpCarCatalogsRequest>): Promise<AuthorizationFilter> {
const authorizationFilters: AuthorizationFilter = {
filters: {},
dataSources: new Map(),
@@ -621,7 +621,7 @@ export default class AuthorizationService {
authorized: false
};
// Check static & dynamic authorization
- await this.canPerformAuthorizationAction(tenant, userToken, Entity.CAR_CATALOG, Action.LIST,
+ await this.canPerformAuthorizationAction(tenant, userToken, Entity.CAR_CATALOG, authAction,
authorizationFilters, filteredRequest, null, true);
return authorizationFilters;
}

MODIFY
src/server/rest/v1/service/CarService.ts
src/server/rest/v1/service/CarService.ts
import { Action, Entity } from '../../../../types/Authorization';
import { AsyncTaskType, AsyncTasks } from '../../../../types/AsyncTask';
-import { Car, CarCatalog } from '../../../../types/Car';
import { CarCatalogDataResult, CarDataResult } from '../../../../types/DataResult';
import { HTTPAuthError, HTTPError } from '../../../../types/HTTPError';
import { NextFunction, Request, Response } from 'express';
@@ -11,12 +10,14 @@ import AppError from '../../../../exception/AppError';
import AsyncTaskBuilder from '../../../../async-task/AsyncTaskBuilder';
import AuthorizationService from './AuthorizationService';
import Authorizations from '../../../../authorization/Authorizations';
+import { Car } from '../../../../types/Car';
import CarStorage from '../../../../storage/mongodb/CarStorage';
import CarValidator from '../validator/CarValidator';
import Constants from '../../../../utils/Constants';
import LockingHelper from '../../../../locking/LockingHelper';
import LockingManager from '../../../../locking/LockingManager';
import Logging from '../../../../utils/Logging';
+import LoggingHelper from '../../../../utils/LoggingHelper';
import { ServerAction } from '../../../../types/Server';
import Utils from '../../../../utils/Utils';
import UtilsService from './UtilsService';
@@ -33,7 +34,7 @@ export default class CarService {
// Filter
const filteredRequest = CarValidator.getInstance().validateCarCatalogsGetReq(req.query);
// Check auth
- const authorizationCarCatalogsFilter = await AuthorizationService.checkAndGetCarCatalogsAuthorizations(req.tenant, req.user, filteredRequest);
+ const authorizationCarCatalogsFilter = await AuthorizationService.checkAndGetCarCatalogsAuthorizations(req.tenant, req.user, Action.LIST, filteredRequest);
if (!authorizationCarCatalogsFilter.authorized) {
UtilsService.sendEmptyDataResult(res, next);
return;
@@ -44,6 +45,7 @@ export default class CarService {
search: filteredRequest.Search,
carMaker: filteredRequest.CarMaker ? filteredRequest.CarMaker.split('|') : null,
withImage: true,
+ ...authorizationCarCatalogsFilter.filters
},
{
limit: filteredRequest.Limit,
@@ -80,10 +82,8 @@ export default class CarService {
}
public static async handleGetCarCatalogImage(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
- // Unprotected Endpoint: No JWT token is provided
- // Filter
+ // This endpoint is not protected, so no need to check user's access
const filteredRequest = CarValidator.getInstance().validateCarCatalogGetReq(req.query);
- UtilsService.assertIdIsProvided(action, filteredRequest.ID, MODULE_NAME, 'handleGetCarCatalogImage', req.user);
// Get the car Image
const carCatalog = await CarStorage.getCarCatalogImage(filteredRequest.ID);
if (carCatalog?.image) {
@@ -97,7 +97,7 @@ export default class CarService {
}
// Revert to binary
res.setHeader('content-type', header);
- res.send(Buffer.from(carCatalog.image, encoding));
+ res.send(carCatalog.image ? Buffer.from(carCatalog.image, encoding) : null);
} else {
res.send(null);
}
@@ -112,8 +112,6 @@ export default class CarService {
}
// Filter
const filteredRequest = CarValidator.getInstance().validateCarCatalogImagesGetReq(req.query);
- // Check mandatory fields
- UtilsService.assertIdIsProvided(action, filteredRequest.ID, MODULE_NAME, 'handleGetCarCatalogImages', req.user);
// Check dynamic auth
const authorizationFilter = await AuthorizationService.checkAndGetCarCatalogAuthorizations(
req.tenant, req.user, { ID: filteredRequest.ID }, Action.READ);
@@ -142,7 +140,8 @@ export default class CarService {
Action.SYNCHRONIZE, Entity.CAR_CATALOG, MODULE_NAME, 'handleSynchronizeCarCatalogs');
}
// Check auth
- if (!await Authorizations.canSynchronizeCarCatalogs(req.user)) {
+ const authorizationFilter = await AuthorizationService.checkAndGetCarCatalogsAuthorizations(req.tenant, req.user, Action.SYNCHRONIZE);
+ if (!authorizationFilter.authorized) {
throw new AppAuthError({
errorCode: HTTPAuthError.FORBIDDEN,
user: req.user,
@@ -188,7 +187,8 @@ export default class CarService {
// Filter
const filteredRequest = CarValidator.getInstance().validateCarMakersGetReq(req.query);
// Check auth
- if (!await Authorizations.canListCarCatalogs(req.user)) {
+ const authorizationCarCatalogsFilter = await AuthorizationService.checkAndGetCarCatalogsAuthorizations(req.tenant, req.user, Action.LIST, filteredRequest);
+ if (!authorizationCarCatalogsFilter.authorized) {
throw new AppAuthError({
errorCode: HTTPAuthError.FORBIDDEN,
user: req.user,
@@ -262,6 +262,7 @@ export default class CarService {
newCar.id = await CarStorage.saveCar(req.tenant, newCar);
await Logging.logInfo({
tenantID: req.user.tenantID,
+ ...LoggingHelper.getCarProperties(newCar),
user: req.user, module: MODULE_NAME, method: 'handleCreateCar',
message: `Car with VIN '${newCar.vin}' and plate ID '${newCar.licensePlate}' has been created successfully`,
action: action,
@@ -343,6 +344,7 @@ export default class CarService {
}
await Logging.logInfo({
tenantID: req.user.tenantID,
+ ...LoggingHelper.getCarProperties(car),
user: req.user, module: MODULE_NAME, method: 'handleUpdateCar',
message: `Car '${car.id}' has been updated successfully`,
action: action,
@@ -422,6 +424,7 @@ export default class CarService {
await CarService.setDefaultCarForUser(req.tenant, car.userID);
}
await Logging.logInfo({
+ ...LoggingHelper.getCarProperties(car),
tenantID: req.user.tenantID,
user: req.user, module: MODULE_NAME, method: 'handleDeleteCar',
message: `Car '${Utils.buildCarName(car)}' has been deleted successfully`,

MODIFY
src/server/rest/v1/service/UtilsService.ts
src/server/rest/v1/service/UtilsService.ts
@@ -832,9 +832,9 @@ export default class UtilsService {
throw new AppAuthError({
errorCode: HTTPAuthError.FORBIDDEN,
user: userToken,
- action: authAction, entity: Entity.USER,
- module: MODULE_NAME, method: 'checkAndGetUserAuthorization',
- value: carCatalogID.toString()
+ action: authAction, entity: Entity.CAR_CATALOG,
+ module: MODULE_NAME, method: 'checkAndGetCarCatalogAuthorization',
+ value: carCatalogID.toString(),
});
}
return carCatalog;

MODIFY
src/utils/LoggingHelper.ts
src/utils/LoggingHelper.ts
import Asset from '../types/Asset';
+import { Car } from '../types/Car';
import ChargingStation from '../types/ChargingStation';
import Site from '../types/Site';
import SiteArea from '../types/SiteArea';
@@ -46,4 +47,12 @@ export default class LoggingHelper {
siteID: siteArea.siteID,
};
}
+
+ public static getCarProperties(car: Car): { carID: string; userID: string, carCatalogID: number } {
+ return {
+ carID: car.id,
+ userID: car.userID,
+ carCatalogID: car.carCatalogID
+ };
+ }
}

Align Car service with ABAC auth

- Use AuthService instead of Authorizations class
- Small clean + alignement
- Added loghelper for car