Comply with PR remarks
MODIFY
src/authorization/AuthorizationsDefinition.ts
src/authorization/AuthorizationsDefinition.ts
@@ -347,17 +347,22 @@ export const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
Fn: 'custom:dynamicAuthorizations',
args: {
asserts: [],
- filters: ['DynamicAsset']
- }
+ filters: []
}
},
+ attributes: [
+ 'id', 'name', 'siteAreaID', 'siteArea.id', 'siteArea.name', 'siteArea.siteID', 'siteID', 'assetType', 'coordinates',
+ 'dynamicAsset', 'usesPushAPI', 'connectionID', 'meterID', 'currentInstantWatts', 'currentStateOfCharge', 'issuer',
+ 'startedAt', 'instantWatts', 'instantAmps', 'limitWatts', 'limitAmps', 'endedAt', 'stateOfCharge'
+ ]
+ },
{
resource: Entity.ASSET, action: Action.RETRIEVE_CONSUMPTION,
condition: {
Fn: 'custom:dynamicAuthorizations',
args: {
asserts: [],
- filters: ['DynamicAsset', '-UsesPushAPI']
+ filters: []
}
}
},
@@ -825,10 +830,15 @@ export const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
Fn: 'custom:dynamicAuthorizations',
args: {
asserts: [],
- filters: ['DynamicAsset']
- }
+ filters: []
}
},
+ attributes: [
+ 'id', 'name', 'siteAreaID', 'siteArea.id', 'siteArea.name', 'siteArea.siteID', 'siteID', 'assetType', 'coordinates',
+ 'dynamicAsset', 'connectionID', 'meterID', 'currentInstantWatts', 'currentStateOfCharge',
+ 'startedAt', 'instantWatts', 'instantAmps', 'limitWatts', 'limitAmps', 'endedAt', 'stateOfCharge'
+ ]
+ },
{ resource: Entity.SETTING, action: Action.READ },
{
resource: Entity.CAR_CATALOG, action: Action.LIST,
@@ -1111,12 +1121,13 @@ export const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
Fn: 'custom:dynamicAuthorizations',
args: {
asserts: [],
- filters: ['AssignedSites', 'LocalIssuer','DynamicAsset']
+ filters: ['AssignedSites', 'LocalIssuer']
}
},
attributes: [
'id', 'name', 'siteAreaID', 'siteArea.id', 'siteArea.name', 'siteArea.siteID', 'siteID', 'assetType', 'coordinates',
- 'dynamicAsset', 'connectionID', 'meterID', 'currentInstantWatts', 'currentStateOfCharge'
+ 'dynamicAsset', 'connectionID', 'meterID', 'currentInstantWatts', 'currentStateOfCharge',
+ 'startedAt', 'instantWatts', 'instantAmps', 'limitWatts', 'limitAmps', 'endedAt', 'stateOfCharge'
],
},
{

MODIFY
src/authorization/DynamicAuthorizationFactory.ts
src/authorization/DynamicAuthorizationFactory.ts
@@ -5,7 +5,6 @@ import AssignedSitesCompaniesDynamicAuthorizationFilter from './dynamic-filters/
import AssignedSitesDynamicAuthorizationDataSource from './dynamic-data-source/AssignedSitesDynamicAuthorizationDataSource';
import AssignedSitesDynamicAuthorizationFilter from './dynamic-filters/AssignedSitesDynamicAuthorizationFilter';
import BasicUserDynamicAuthorizationAssert from './dynamic-assert/BasicUserDynamicAuthorizationAssert';
-import DynamicAssetDynamicAuthorizationFilter from './dynamic-filters/DynamicAssetDynamicAuthorizationFilter';
import DynamicAuthorizationAssert from './DynamicAuthorizationAssert';
import DynamicAuthorizationDataSource from './DynamicAuthorizationDataSource';
import DynamicAuthorizationFilter from './DynamicAuthorizationFilter';
@@ -24,7 +23,6 @@ import SitesOwnerDynamicAuthorizationFilter from './dynamic-filters/SitesOwnerDy
import Tenant from '../types/Tenant';
import UserMandatoryDynamicAuthorizationAssert from './dynamic-assert/UserMandatoryDynamicAuthorizationAssert';
import UserToken from '../types/UserToken';
-import UsesPushAPIDynamicAuthorizationFilter from './dynamic-filters/UsesPushAPIDynamicAuthorizationFilter';
export default class DynamicAuthorizationFactory {
public static async getDynamicFilter(tenant: Tenant, userToken: UserToken,
@@ -61,12 +59,6 @@ export default class DynamicAuthorizationFactory {
case DynamicAuthorizationFilterName.LOCAL_ISSUER:
dynamicFilter = new LocalIssuerDynamicAuthorizationFilter(tenant, userToken, negateFilter);
break;
- case DynamicAuthorizationFilterName.DYNAMIC_ASSET:
- dynamicFilter = new DynamicAssetDynamicAuthorizationFilter(tenant, userToken, negateFilter);
- break;
- case DynamicAuthorizationFilterName.USES_PUSH_API:
- dynamicFilter = new UsesPushAPIDynamicAuthorizationFilter(tenant, userToken, negateFilter);
- break;
}
// Init Data Source
if (dynamicFilter) {

DELETE
src/authorization/dynamic-filters/DynamicAssetDynamicAuthorizationFilter.ts
-import { AuthorizationFilter, DynamicAuthorizationDataSourceName, Entity } from '../../types/Authorization';
-
-import DynamicAuthorizationFilter from '../DynamicAuthorizationFilter';
-import { EntityData } from '../../types/GlobalType';
-import Utils from '../../utils/Utils';
-
-export default class DynamicAssetDynamicAuthorizationFilter extends DynamicAuthorizationFilter {
- public processFilter(authorizationFilters: AuthorizationFilter, extraFilters: Record<string, any>, entityData?: EntityData): void {
- authorizationFilters.authorized = true;
- if (Utils.objectHasProperty(entityData, 'dynamicAsset')) {
- // Dynamic asset has true value
- authorizationFilters.authorized = entityData['dynamicAsset'];
- }
- }
-
- public getApplicableEntities(): Entity[] {
- return [
- Entity.ASSET
- ];
- }
-
- public getApplicableDataSources(): DynamicAuthorizationDataSourceName[] {
- return [];
- }
-}

DELETE
src/authorization/dynamic-filters/UsesPushAPIDynamicAuthorizationFilter.ts
-import { AuthorizationFilter, DynamicAuthorizationDataSourceName, Entity } from '../../types/Authorization';
-
-import DynamicAuthorizationFilter from '../DynamicAuthorizationFilter';
-import { EntityData } from '../../types/GlobalType';
-import Utils from '../../utils/Utils';
-
-export default class UsesPushAPIDynamicAuthorizationFilter extends DynamicAuthorizationFilter {
- public processFilter(authorizationFilters: AuthorizationFilter, extraFilters: Record<string, any>, entityData?: EntityData): void {
- authorizationFilters.authorized = true;
- if (Utils.objectHasProperty(entityData, 'usesPushAPI')) {
- // usesPushAPI has true value
- authorizationFilters.authorized = entityData['usesPushAPI'];
- }
- }
-
- public getApplicableEntities(): Entity[] {
- return [
- Entity.ASSET
- ];
- }
-
- public getApplicableDataSources(): DynamicAuthorizationDataSourceName[] {
- return [];
- }
-}

MODIFY
src/server/rest/v1/service/AssetService.ts
src/server/rest/v1/service/AssetService.ts
-import { Action, AuthorizationFilter, Entity } from '../../../../types/Authorization';
+import { Action, Entity } from '../../../../types/Authorization';
+import { HTTPAuthError, HTTPError } from '../../../../types/HTTPError';
import { NextFunction, Request, Response } from 'express';
-import Tenant, { TenantComponents } from '../../../../types/Tenant';
+import AppAuthError from '../../../../exception/AppAuthError';
import AppError from '../../../../exception/AppError';
import Asset from '../../../../types/Asset';
import { AssetDataResult } from '../../../../types/DataResult';
@@ -13,13 +14,14 @@ import AuthorizationService from './AuthorizationService';
import Constants from '../../../../utils/Constants';
import Consumption from '../../../../types/Consumption';
import ConsumptionStorage from '../../../../storage/mongodb/ConsumptionStorage';
-import { HTTPError } from '../../../../types/HTTPError';
import Logging from '../../../../utils/Logging';
import OCPPUtils from '../../../../server/ocpp/utils/OCPPUtils';
import { ServerAction } from '../../../../types/Server';
import SiteArea from '../../../../types/SiteArea';
import { StatusCodes } from 'http-status-codes';
+import { TenantComponents } from '../../../../types/Tenant';
import TenantStorage from '../../../../storage/mongodb/TenantStorage';
+import UserToken from '../../../../types/UserToken';
import Utils from '../../../../utils/Utils';
import UtilsService from './UtilsService';
import moment from 'moment';
@@ -34,16 +36,8 @@ export default class AssetService {
Action.READ_CONSUMPTION, Entity.ASSET, MODULE_NAME, 'handleGetAssetConsumption');
// Filter
const filteredRequest = AssetValidator.getInstance().validateAssetGetConsumptionsReq(req.query);
- UtilsService.assertIdIsProvided(action, filteredRequest.AssetID, MODULE_NAME,
- 'handleGetAssetConsumption', req.user);
- // Retrieve authorizations for current action attempt
- const actionOnAssetAuthorizationFilter = await AuthorizationService.checkAndGetAssetAuthorizations(req.tenant, req.user, Action.READ_CONSUMPTION, {});
- // Get asset
- const asset = await AssetService.getAssetFromStorage(req.tenant, filteredRequest.AssetID, { dynamicOnly: true }, actionOnAssetAuthorizationFilter);
- UtilsService.assertObjectExists(action, asset, `Asset ID '${filteredRequest.AssetID}' cannot be retrieved`,
- MODULE_NAME, 'handleGetAssetConsumption', req.user);
- // Check dynamic auth
- await AuthorizationService.checkAndGetAssetAuthorizations(req.tenant, req.user, Action.READ_CONSUMPTION, {}, asset);
+ // Check and get Asset
+ const asset = await UtilsService.checkAndGetAssetAuthorization(req.tenant, req.user, filteredRequest.AssetID, Action.READ_CONSUMPTION, action, null, {}, true);
// Check dates
if (!filteredRequest.StartDate || !filteredRequest.EndDate) {
throw new AppError({
@@ -55,22 +49,13 @@ export default class AssetService {
});
}
// Check dates order
- if (filteredRequest.StartDate && filteredRequest.EndDate &&
- moment(filteredRequest.StartDate).isAfter(moment(filteredRequest.EndDate))) {
- throw new AppError({
- errorCode: HTTPError.GENERAL_ERROR,
- message: `The requested start date '${filteredRequest.StartDate.toISOString()}' is after the end date '${filteredRequest.EndDate.toISOString()}' `,
- module: MODULE_NAME, method: 'handleGetAssetConsumption',
- user: req.user,
- action: action
- });
- }
+ AssetService.checkDateOrder(filteredRequest.StartDate, filteredRequest.EndDate, req.user, action, 'handleGetAssetConsumption');
// Get the ConsumptionValues
const consumptions = await ConsumptionStorage.getAssetConsumptions(req.tenant, {
assetID: filteredRequest.AssetID,
startDate: filteredRequest.StartDate,
endDate: filteredRequest.EndDate
- }, ['startedAt', 'instantWatts', 'instantAmps', 'limitWatts', 'limitAmps', 'endedAt', 'stateOfCharge']);
+ }, asset.projectFields);
// Assign
asset.values = consumptions;
res.json(asset);
@@ -79,20 +64,11 @@ export default class AssetService {
public static async handleCreateAssetConsumption(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
// Check if component is active
- UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.ASSET,
- Action.CREATE_CONSUMPTION, Entity.ASSET, MODULE_NAME, 'handleCreateAssetConsumption');
+ UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.ASSET, Action.CREATE_CONSUMPTION, Entity.ASSET, MODULE_NAME, 'handleCreateAssetConsumption');
// Validate request
const filteredRequest = AssetValidator.getInstance().validateAssetConsumptionCreateReq({ ...req.query, ...req.body });
- UtilsService.assertIdIsProvided(action, filteredRequest.assetID, MODULE_NAME,
- 'handleCreateAssetConsumption', req.user);
- // Retrieve authorizations for current action attempt
- const actionOnAssetAuthorizationFilter = await AuthorizationService.checkAndGetAssetAuthorizations(req.tenant, req.user, Action.CREATE_CONSUMPTION, {});
- // Get Asset
- const asset = await AssetService.getAssetFromStorage(req.tenant, filteredRequest.assetID, { withSiteArea: true }, actionOnAssetAuthorizationFilter);
- UtilsService.assertObjectExists(action, asset, `Asset ID '${filteredRequest.assetID}' cannot be retrieved to create consumption`,
- MODULE_NAME, 'handleCreateAssetConsumption', req.user);
- // Check dynamic auth
- await AuthorizationService.checkAndGetAssetAuthorizations(req.tenant, req.user, Action.CREATE_CONSUMPTION, {}, asset);
+ // Check and get Asset
+ const asset = await UtilsService.checkAndGetAssetAuthorization(req.tenant, req.user, filteredRequest.assetID, Action.CREATE_CONSUMPTION, action, null, { withSiteArea: true });
// Check if connection ID exists
if (!Utils.isNullOrUndefined(asset.connectionID)) {
throw new AppError({
@@ -104,16 +80,7 @@ export default class AssetService {
});
}
// Check dates order
- if (filteredRequest.startedAt && filteredRequest.endedAt &&
- !moment(filteredRequest.endedAt).isAfter(moment(filteredRequest.startedAt))) {
- throw new AppError({
- errorCode: HTTPError.GENERAL_ERROR,
- message: `The requested start date '${moment(filteredRequest.startedAt).toISOString()}' is after the end date '${moment(filteredRequest.endedAt).toISOString()}' `,
- module: MODULE_NAME, method: 'handleCreateAssetConsumption',
- user: req.user,
- action: action
- });
- }
+ AssetService.checkDateOrder(filteredRequest.startedAt, filteredRequest.endedAt, req.user, action, 'handleCreateAssetConsumption');
// Get latest consumption and check dates
const lastConsumption = await ConsumptionStorage.getLastAssetConsumption(req.tenant, { assetID: filteredRequest.assetID });
if (!Utils.isNullOrUndefined(lastConsumption)) {
@@ -173,10 +140,10 @@ export default class AssetService {
// Check if component is active
UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.ASSET,
Action.CHECK_CONNECTION, Entity.ASSET, MODULE_NAME, 'handleCheckAssetConnection');
- // Filter request
+ // Filter request, NOTE: ID in this request is a connection ID not an Asset ID
const filteredRequest = AssetValidator.getInstance().validateAssetCheckConnectionReq(req.query);
// Check dynamic auth
- await AuthorizationService.checkAndGetAssetAuthorizations(req.tenant, req.user, Action.CHECK_CONNECTION, filteredRequest);
+ await AuthorizationService.checkAndGetAssetsAuthorizations(req.tenant, req.user, Action.CHECK_CONNECTION);
// Get asset connection type
const assetImpl = await AssetFactory.getAssetImpl(req.tenant, filteredRequest.ID);
// Asset has unknown connection type
@@ -216,16 +183,11 @@ export default class AssetService {
Action.RETRIEVE_CONSUMPTION, Entity.ASSET, MODULE_NAME, 'handleRetrieveConsumption');
// Filter request
const assetID = AssetValidator.getInstance().validateAssetGetReq(req.query).ID;
- UtilsService.assertIdIsProvided(action, assetID, MODULE_NAME, 'handleRetrieveConsumption', req.user);
- // Retrieve authorizations for current action attempt
- const actionOnAssetAuthorizationFilter = await AuthorizationService.checkAndGetAssetAuthorizations(
- req.tenant, req.user, Action.RETRIEVE_CONSUMPTION, {});
- // Get
- const asset = await AssetService.getAssetFromStorage(req.tenant, assetID, { dynamicOnly: true, usesPushAPI: false }, actionOnAssetAuthorizationFilter);
+ // Check and get Asset
+ const asset = await UtilsService.checkAndGetAssetAuthorization(req.tenant, req.user, assetID, Action.RETRIEVE_CONSUMPTION, action, null,
+ { }, true);
UtilsService.assertObjectExists(action, asset, `Asset ID '${assetID}' consumption cannot be retrieved`,
MODULE_NAME, 'handleRetrieveConsumption', req.user);
- // Dynamic auth
- await AuthorizationService.checkAndGetAssetAuthorizations(req.tenant, req.user, Action.RETRIEVE_CONSUMPTION, {}, asset);
// Get asset factory
const assetImpl = await AssetFactory.getAssetImpl(req.tenant, asset.connectionID);
if (!assetImpl) {
@@ -274,8 +236,11 @@ export default class AssetService {
// Filter
const filteredRequest = AssetValidator.getInstance().validateAssetsGetReq(req.query);
// Check dynamic auth
- const authorizationAssetsInErrorFilter = await AuthorizationService.checkAndGetAssetsAuthorizations(
- req.tenant, req.user, Action.IN_ERROR, filteredRequest);
+ const authorizationAssetsInErrorFilter = await AuthorizationService.checkAndGetAssetsAuthorizations(req.tenant, req.user, Action.IN_ERROR, filteredRequest);
+ if (!authorizationAssetsInErrorFilter.authorized) {
+ UtilsService.sendEmptyDataResult(res, next);
+ return;
+ }
// Get the assets
const assets = await AssetStorage.getAssetsInError(req.tenant,
{
@@ -307,17 +272,8 @@ export default class AssetService {
Action.DELETE, Entity.ASSET, MODULE_NAME, 'handleDeleteAsset');
// Filter
const filteredRequest = AssetValidator.getInstance().validateAssetGetReq(req.query);
- // Check Mandatory fields
- UtilsService.assertIdIsProvided(action, filteredRequest.ID, MODULE_NAME, 'handleDeleteAsset', req.user);
- // Retrieve authorizations for current action attempt
- const actionOnAssetAuthorizationFilter = await AuthorizationService.checkAndGetAssetAuthorizations(
- req.tenant, req.user, Action.DELETE, filteredRequest);
- // Get
- const asset = await AssetService.getAssetFromStorage(req.tenant, filteredRequest.ID, { withSiteArea: filteredRequest.WithSiteArea }, actionOnAssetAuthorizationFilter);
- UtilsService.assertObjectExists(action, asset, `Asset ID '${filteredRequest.ID}' cannot be deleted`,
- MODULE_NAME, 'handleDeleteAsset', req.user);
- // Dynamic auth
- await AuthorizationService.checkAndGetAssetAuthorizations(req.tenant, req.user, Action.DELETE, filteredRequest, asset);
+ // Check and get Asset
+ const asset = await UtilsService.checkAndGetAssetAuthorization(req.tenant, req.user, filteredRequest.ID, Action.DELETE, action, null, { withSiteArea: filteredRequest.WithSiteArea });
// Delete
await AssetStorage.deleteAsset(req.tenant, asset.id);
// Log
@@ -339,25 +295,15 @@ export default class AssetService {
Action.READ, Entity.ASSET, MODULE_NAME, 'handleGetAsset');
// Filter
const filteredRequest = AssetValidator.getInstance().validateAssetGetReq(req.query);
- // ID is mandatory
- UtilsService.assertIdIsProvided(action, filteredRequest.ID, MODULE_NAME, 'handleGetAsset', req.user);
- // Check action authorization before querying storage
- const actionOnAssetAuthorizationFilter = await AuthorizationService.checkAndGetAssetAuthorizations(
- req.tenant, req.user, Action.READ, filteredRequest);
- // Get it
- const asset = await AssetService.getAssetFromStorage(req.tenant, filteredRequest.ID, { withSiteArea: filteredRequest.WithSiteArea }, actionOnAssetAuthorizationFilter);
- UtilsService.assertObjectExists(action, asset, `Asset ID '${filteredRequest.ID}' does not exist`,
- MODULE_NAME, 'handleGetAsset', req.user);
- // Dynamic auth
- const authorizationAssetsFilter = await AuthorizationService.checkAndGetAssetAuthorizations(req.tenant, req.user, Action.READ, filteredRequest, asset);
- // Add authorizations flags
- await AuthorizationService.addAssetAuthorizations(req.tenant, req.user, asset, authorizationAssetsFilter);
+ // Check and get Asset
+ const asset = await UtilsService.checkAndGetAssetAuthorization(req.tenant, req.user, filteredRequest.ID, Action.READ, action, null,
+ { withSiteArea: filteredRequest.WithSiteArea }, true);
res.json(asset);
next();
}
public static async handleGetAssetImage(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
- // Filter
+ // This endpoint is not protected, so no need to check user's access
const filteredRequest = AssetValidator.getInstance().validateAssetGetImageReq(req.query);
// Get the tenant
const tenant = await TenantStorage.getTenant(filteredRequest.TenantID);
@@ -390,6 +336,10 @@ export default class AssetService {
// Check dynamic auth
const authorizationAssetsFilter = await AuthorizationService.checkAndGetAssetsAuthorizations(
req.tenant, req.user, Action.LIST, filteredRequest);
+ if (!authorizationAssetsFilter.authorized) {
+ UtilsService.sendEmptyDataResult(res, next);
+ return;
+ }
// Get the assets
const assets = await AssetStorage.getAssets(req.tenant,
{
@@ -425,7 +375,15 @@ export default class AssetService {
const filteredAssetRequest = AssetValidator.getInstance().validateAssetCreateReq(req.body);
UtilsService.checkIfAssetValid(filteredAssetRequest, req);
// Check authorizations for current action attempt
- await AuthorizationService.checkAndGetAssetAuthorizations(req.tenant, req.user, Action.CREATE, {}, filteredAssetRequest);
+ const authorizationFilter = await AuthorizationService.checkAndGetAssetAuthorizations(req.tenant, req.user, Action.CREATE, {}, filteredAssetRequest);
+ if (!authorizationFilter.authorized) {
+ throw new AppAuthError({
+ errorCode: HTTPAuthError.FORBIDDEN,
+ user: req.user,
+ action: Action.CREATE, entity: Entity.ASSET,
+ module: MODULE_NAME, method: 'handleCreateAsset'
+ });
+ }
// Check Site Area authorization
let siteArea: SiteArea = null;
if (Utils.isComponentActiveFromToken(req.user, TenantComponents.ORGANIZATION) && filteredAssetRequest.siteAreaID) {
@@ -462,20 +420,14 @@ export default class AssetService {
// Filter
const filteredRequest = AssetValidator.getInstance().validateAssetUpdateReq(req.body);
UtilsService.checkIfAssetValid(filteredRequest, req);
- // Retrieve authorizations for current action attempt
- const actionOnAssetAuthorizationFilter = await AuthorizationService.checkAndGetAssetAuthorizations(req.tenant, req.user, Action.UPDATE, {}, filteredRequest);
// Check Site Area authorization
let siteArea: SiteArea = null;
if (Utils.isComponentActiveFromToken(req.user, TenantComponents.ORGANIZATION) && filteredRequest.siteAreaID) {
siteArea = await UtilsService.checkAndGetSiteAreaAuthorization(
req.tenant, req.user, filteredRequest.siteAreaID, Action.UPDATE, action, filteredRequest, null, false);
}
- // Get asset
- const asset = await AssetService.getAssetFromStorage(req.tenant, filteredRequest.id, {}, actionOnAssetAuthorizationFilter);
- UtilsService.assertObjectExists(action, asset, `Asset ID '${filteredRequest.id}' cannot be modified`,
- MODULE_NAME, 'handleUpdateAsset', req.user);
- // Check dynamic auth
- await AuthorizationService.checkAndGetAssetAuthorizations(req.tenant, req.user, Action.UPDATE, {}, asset);
+ // Check and get asset
+ const asset = await UtilsService.checkAndGetAssetAuthorization(req.tenant, req.user, filteredRequest.id, Action.UPDATE, action, filteredRequest);
// Update Asset values and persist
asset.name = filteredRequest.name;
asset.siteAreaID = filteredRequest.siteAreaID;
@@ -507,16 +459,17 @@ export default class AssetService {
next();
}
- private static async getAssetFromStorage(tenant: Tenant, assetID: string, additionalFilters: Record<string, any> = {}, authorizationAssetFilter: AuthorizationFilter): Promise<Asset> {
- // Retrieve Asset from storage
- const asset = await AssetStorage.getAsset(tenant,
- assetID,
- {
- ...additionalFilters,
- ...authorizationAssetFilter.filters
- },
- authorizationAssetFilter.projectFields
- );
- return asset;
+ private static checkDateOrder(startSate: Date, endDate: Date, user: UserToken, action: ServerAction, methodName: string): void {
+ // Check dates order
+ if (startSate && endDate &&
+ !moment(endDate).isAfter(moment(startSate))) {
+ throw new AppError({
+ errorCode: HTTPError.GENERAL_ERROR,
+ message: `The requested start date '${moment(startSate).toISOString()}' is after the end date '${moment(endDate).toISOString()}' `,
+ module: MODULE_NAME, method: methodName,
+ user: user,
+ action: action
+ });
+ }
}
}

MODIFY
src/server/rest/v1/service/AuthorizationService.ts
src/server/rest/v1/service/AuthorizationService.ts
import { Action, AuthorizationActions, AuthorizationContext, AuthorizationFilter, Entity } from '../../../../types/Authorization';
import { AssetDataResult, CarCatalogDataResult, CarDataResult, CompanyDataResult, LogDataResult, PricingDefinitionDataResult, RegistrationTokenDataResult, SiteAreaDataResult, SiteDataResult, TagDataResult, UserDataResult } from '../../../../types/DataResult';
import { Car, CarCatalog } from '../../../../types/Car';
+import { HttpAssetRequest, HttpAssetsRequest } from '../../../../types/requests/HttpAssetRequest';
import { HttpCarCatalogRequest, HttpCarCatalogsRequest, HttpCarRequest, HttpCarsRequest } from '../../../../types/requests/HttpCarRequest';
import { HttpChargingStationRequest, HttpChargingStationsRequest } from '../../../../types/requests/HttpChargingStationRequest';
import { HttpCompaniesRequest, HttpCompanyRequest } from '../../../../types/requests/HttpCompanyRequest';
@@ -22,7 +23,6 @@ import Constants from '../../../../utils/Constants';
import DynamicAuthorizationFactory from '../../../../authorization/DynamicAuthorizationFactory';
import { EntityData } from '../../../../types/GlobalType';
import { HTTPAuthError } from '../../../../types/HTTPError';
-import { HttpAssetsRequest } from '../../../../types/requests/HttpAssetRequest';
import { HttpLogRequest } from '../../../../types/requests/HttpLoggingRequest';
import { HttpRegistrationTokenRequest } from '../../../../types/requests/HttpRegistrationToken';
import { Log } from '../../../../types/Log';
@@ -265,17 +265,9 @@ export default class AuthorizationService {
}
public static async checkAndGetAssetAuthorizations(tenant: Tenant, userToken: UserToken, authAction: Action,
- filteredRequest: Partial<HttpCompaniesRequest>, entityData?: EntityData): Promise<AuthorizationFilter> {
- const authorizationFilters: AuthorizationFilter = {
- filters: {},
- dataSources: new Map(),
- projectFields: [],
- authorized: false,
- };
- // Check static & dynamic authorization
- await AuthorizationService.canPerformAuthorizationAction(tenant, userToken, Entity.ASSET, authAction,
- authorizationFilters, filteredRequest, entityData, true);
- return authorizationFilters;
+ filteredRequest: Partial<HttpAssetRequest>, entityData?: EntityData): Promise<AuthorizationFilter> {
+ return AuthorizationService.checkAndGetEntityAuthorizations(
+ tenant, Entity.ASSET, userToken, filteredRequest, filteredRequest.ID ? { AssetID: filteredRequest.ID } : {}, authAction, entityData);
}
public static async addAssetsAuthorizations(tenant: Tenant, userToken: UserToken, assets: AssetDataResult, authorizationFilter: AuthorizationFilter): Promise<void> {
@@ -292,9 +284,12 @@ export default class AuthorizationService {
asset.canRead = true; // Always true as it should be filtered upfront
asset.canDelete = await AuthorizationService.canPerformAuthorizationAction(tenant, userToken, Entity.ASSET, Action.DELETE, authorizationFilter, {}, asset);
asset.canUpdate = await AuthorizationService.canPerformAuthorizationAction(tenant, userToken, Entity.ASSET, Action.UPDATE, authorizationFilter, {}, asset);
- asset.canRetrieveConsumption = await AuthorizationService.canPerformAuthorizationAction(tenant, userToken, Entity.ASSET, Action.RETRIEVE_CONSUMPTION, authorizationFilter, {}, asset);
- asset.canReadConsumption = await AuthorizationService.canPerformAuthorizationAction(tenant, userToken, Entity.ASSET, Action.READ_CONSUMPTION, authorizationFilter, {}, asset);
asset.canCheckConnection = await AuthorizationService.canPerformAuthorizationAction(tenant, userToken, Entity.ASSET, Action.CHECK_CONNECTION, authorizationFilter, {}, asset);
+ // Additional auth rules based on asset attributes
+ asset.canRetrieveConsumption = asset.dynamicAsset && !asset.usesPushAPI &&
+ await AuthorizationService.canPerformAuthorizationAction(tenant, userToken, Entity.ASSET, Action.RETRIEVE_CONSUMPTION, authorizationFilter, {}, asset);
+ asset.canReadConsumption = asset.dynamicAsset &&
+ await AuthorizationService.canPerformAuthorizationAction(tenant, userToken, Entity.ASSET, Action.READ_CONSUMPTION, authorizationFilter, {}, asset);
// Optimize data over the net
Utils.removeCanPropertiesWithFalseValue(asset);
}

MODIFY
src/server/rest/v1/service/UtilsService.ts
src/server/rest/v1/service/UtilsService.ts
@@ -368,6 +368,61 @@ export default class UtilsService {
return site;
}
+ public static async checkAndGetAssetAuthorization(tenant: Tenant, userToken: UserToken, assetID: string, authAction: Action,
+ action: ServerAction, entityData?: EntityData, additionalFilters: Record<string, any> = {}, applyProjectFields = false): Promise<Asset> {
+ // Check mandatory fields failsafe, should already be done in the json schema validation for each request
+ UtilsService.assertIdIsProvided(action, assetID, MODULE_NAME, 'checkAndGetAssetAuthorization', userToken);
+ // Retrieve authorization for action
+ const authorizationFilter = await AuthorizationService.checkAndGetAssetAuthorizations(tenant, userToken, authAction, { ID: assetID }, entityData);
+ // In certain cases the authorization filter will pass
+ if (!authorizationFilter.authorized) {
+ throw new AppAuthError({
+ errorCode: HTTPAuthError.FORBIDDEN,
+ user: userToken,
+ action: authAction, entity: Entity.SITE,
+ module: MODULE_NAME, method: 'checkAndGetAssetAuthorization',
+ value: assetID,
+ siteID: assetID,
+ });
+ }
+ // Retrieve Asset from storage
+ const asset = await AssetStorage.getAsset(tenant,
+ assetID,
+ {
+ ...additionalFilters,
+ ...authorizationFilter.filters
+ },
+ applyProjectFields ? authorizationFilter.projectFields : null
+ );
+ // Check object not empty
+ UtilsService.assertObjectExists(action, asset, `Asset ID '${assetID}' cannot be retrieved`,
+ MODULE_NAME, 'checkAndGetAssetAuthorization', userToken);
+
+ // Assign projected fields
+ if (authorizationFilter.projectFields) {
+ asset.projectFields = authorizationFilter.projectFields;
+ }
+ // Assign Metadata
+ if (authorizationFilter.metadata) {
+ asset.metadata = authorizationFilter.metadata;
+ }
+ // Add entity authorization
+ await AuthorizationService.addAssetAuthorizations(tenant, userToken, asset, authorizationFilter);
+ // Check authorization on retrieved entity
+ const authorized = AuthorizationService.canPerformAction(asset, authAction);
+ if (!authorized) {
+ throw new AppAuthError({
+ errorCode: HTTPAuthError.FORBIDDEN,
+ user: userToken,
+ action: authAction, entity: Entity.SITE,
+ module: MODULE_NAME, method: 'checkAndGetAssetAuthorization',
+ value: assetID,
+ siteID: assetID,
+ });
+ }
+ return asset;
+ }
+
public static async checkAndGetLogAuthorization(tenant: Tenant, userToken: UserToken, logID: string, authAction: Action,
action: ServerAction, entityData?: EntityData, additionalFilters: Record<string, any> = {}, applyProjectFields = false): Promise<Log> {
// Check mandatory fields

MODIFY
src/storage/mongodb/AssetStorage.ts
src/storage/mongodb/AssetStorage.ts
@@ -15,14 +15,12 @@ const MODULE_NAME = 'AssetStorage';
export default class AssetStorage {
public static async getAsset(tenant: Tenant, id: string = Constants.UNKNOWN_OBJECT_ID,
- params: { withSiteArea?: boolean, siteIDs?: string[], issuer?: boolean, dynamicOnly?: boolean, usesPushAPI?:boolean } = {}, projectFields?: string[]): Promise<Asset> {
+ params: { withSiteArea?: boolean, siteIDs?: string[], issuer?: boolean } = {}, projectFields?: string[]): Promise<Asset> {
const assetsMDB = await AssetStorage.getAssets(tenant, {
assetIDs: [id],
withSiteArea: params.withSiteArea,
siteIDs: params.siteIDs,
- issuer: params.issuer,
- dynamicOnly: params.dynamicOnly,
- usesPushAPI: params.usesPushAPI
+ issuer: params.issuer
}, Constants.DB_PARAMS_SINGLE_RECORD, projectFields);
return assetsMDB.count === 1 ? assetsMDB.result[0] : null;
}
@@ -100,10 +98,8 @@ export default class AssetStorage {
}
public static async getAssets(tenant: Tenant,
- params: {
- search?: string; assetIDs?: string[]; siteAreaIDs?: string[]; siteIDs?: string[]; withSiteArea?: boolean;
- withNoSiteArea?: boolean; dynamicOnly?: boolean; issuer?: boolean; usesPushAPI?: boolean;
- } = {},
+ params: { search?: string; assetIDs?: string[]; siteAreaIDs?: string[]; siteIDs?: string[]; withSiteArea?: boolean;
+ withNoSiteArea?: boolean; dynamicOnly?: boolean; issuer?: boolean; } = {},
dbParams?: DbParams, projectFields?: string[]): Promise<DataResult<Asset>> {
const startTime = Logging.traceDatabaseRequestStart();
DatabaseUtils.checkTenantObject(tenant);
@@ -145,10 +141,6 @@ export default class AssetStorage {
if (params.dynamicOnly && Utils.isBoolean(params.dynamicOnly)) {
filters.dynamicAsset = params.dynamicOnly;
}
- // Push API
- if(params.usesPushAPI && Utils.isBoolean(params.usesPushAPI)) {
- filters.usesPushAPI = params.usesPushAPI;
- }
// Limit on Asset for Basic Users
if (!Utils.isEmptyArray(params.assetIDs)) {
filters._id = {

MODIFY
src/types/Authorization.ts
src/types/Authorization.ts
@@ -231,8 +231,6 @@ export enum DynamicAuthorizationFilterName {
OWN_USER = 'OwnUser',
LOCAL_ISSUER = 'LocalIssuer',
EXCLUDE_ACTION = 'ExcludeAction',
- DYNAMIC_ASSET = 'DynamicAsset',
- USES_PUSH_API = 'UsesPushAPI'
}
export enum DynamicAuthorizationAssertName {

Comply with PR remarks