Change assignChargingStationsToSiteArea logic.
MODIFY
src/authorization/AuthorizationsDefinition.ts
src/authorization/AuthorizationsDefinition.ts
@@ -58,7 +58,17 @@ const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
},
{ resource: Entity.SITE_AREA, action: [Action.CREATE, Action.UPDATE, Action.DELETE,
Action.ASSIGN_ASSETS, Action.UNASSIGN_ASSETS, Action.ASSIGN_CHARGING_STATIONS, Action.UNASSIGN_CHARGING_STATIONS], attributes: ['*'] },
- { resource: Entity.CHARGING_STATIONS, action: [Action.LIST, Action.IN_ERROR], attributes: ['*'] },
+ {
+ resource: Entity.CHARGING_STATIONS, action: [Action.LIST, Action.IN_ERROR],
+ attributes: [
+ 'id', 'inactive', 'public', 'chargingStationURL', 'issuer', 'maximumPower', 'excludeFromSmartCharging', 'lastReboot',
+ 'siteAreaID', 'siteArea.id', 'siteArea.name', 'siteArea.smartCharging', 'siteArea.siteID',
+ 'siteArea.site.id', 'siteArea.site.name', 'siteID', 'voltage', 'coordinates', 'forceInactive', 'manualConfiguration', 'firmwareUpdateStatus',
+ 'capabilities', 'endpoint', 'chargePointVendor', 'chargePointModel', 'ocppVersion', 'ocppProtocol', 'lastSeen',
+ 'firmwareVersion', 'currentIPAddress', 'ocppStandardParameters', 'ocppVendorParameters', 'connectors', 'chargePoints',
+ 'createdOn', 'chargeBoxSerialNumber', 'chargePointSerialNumber', 'powerLimitUnit'
+ ]
+ },
{
resource: Entity.CHARGING_STATION, action: [Action.CREATE, Action.READ, Action.UPDATE, Action.DELETE,
Action.RESET, Action.CLEAR_CACHE, Action.GET_CONFIGURATION, Action.CHANGE_CONFIGURATION,
@@ -192,7 +202,17 @@ const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
'voltage', 'smartCharging', 'accessControl', 'connectorStats', 'siteID', 'site.name'
]
},
- { resource: Entity.CHARGING_STATIONS, action: Action.LIST, attributes: ['*'] },
+ {
+ resource: Entity.CHARGING_STATIONS, action: Action.LIST,
+ attributes: [
+ 'id', 'inactive', 'public', 'chargingStationURL', 'issuer', 'maximumPower', 'excludeFromSmartCharging', 'lastReboot',
+ 'siteAreaID', 'siteArea.id', 'siteArea.name', 'siteArea.smartCharging', 'siteArea.siteID',
+ 'siteArea.site.id', 'siteArea.site.name', 'siteID', 'voltage', 'coordinates', 'forceInactive', 'manualConfiguration', 'firmwareUpdateStatus',
+ 'capabilities', 'endpoint', 'chargePointVendor', 'chargePointModel', 'ocppVersion', 'ocppProtocol', 'lastSeen',
+ 'firmwareVersion', 'currentIPAddress', 'ocppStandardParameters', 'ocppVendorParameters', 'connectors', 'chargePoints',
+ 'createdOn', 'chargeBoxSerialNumber', 'chargePointSerialNumber', 'powerLimitUnit'
+ ]
+ },
{ resource: Entity.CHARGING_STATION, action: [Action.READ], attributes: ['*'] },
{
resource: Entity.CHARGING_STATION,
@@ -294,7 +314,17 @@ const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
'voltage', 'smartCharging', 'accessControl', 'connectorStats', 'siteID', 'site.name'
]
},
- { resource: Entity.CHARGING_STATIONS, action: Action.LIST, attributes: ['*'] },
+ {
+ resource: Entity.CHARGING_STATIONS, action: Action.LIST,
+ attributes: [
+ 'id', 'inactive', 'public', 'chargingStationURL', 'issuer', 'maximumPower', 'excludeFromSmartCharging', 'lastReboot',
+ 'siteAreaID', 'siteArea.id', 'siteArea.name', 'siteArea.smartCharging', 'siteArea.siteID',
+ 'siteArea.site.id', 'siteArea.site.name', 'siteID', 'voltage', 'coordinates', 'forceInactive', 'manualConfiguration', 'firmwareUpdateStatus',
+ 'capabilities', 'endpoint', 'chargePointVendor', 'chargePointModel', 'ocppVersion', 'ocppProtocol', 'lastSeen',
+ 'firmwareVersion', 'currentIPAddress', 'ocppStandardParameters', 'ocppVendorParameters', 'connectors', 'chargePoints',
+ 'createdOn', 'chargeBoxSerialNumber', 'chargePointSerialNumber', 'powerLimitUnit'
+ ]
+ },
{ resource: Entity.CHARGING_STATION, action: Action.READ, attributes: ['*'] },
{ resource: Entity.TRANSACTIONS, action: Action.LIST, attributes: ['*'] },
{

MODIFY
src/server/rest/v1/service/AuthorizationService.ts
src/server/rest/v1/service/AuthorizationService.ts
@@ -43,6 +43,10 @@ export default class AuthorizationService {
return entity.canCreate;
case Action.DELETE:
return entity.canDelete;
+ case Action.ASSIGN_CHARGING_STATIONS:
+ return entity['can' + Action.ASSIGN_CHARGING_STATIONS];
+ case Action.UNASSIGN_CHARGING_STATIONS:
+ return entity['can' + Action.UNASSIGN_CHARGING_STATIONS];
default:
return false;
}
@@ -258,46 +262,36 @@ export default class AuthorizationService {
}
public static async checkAssignSiteAreaChargingStationsAuthorizationFilters(
- tenant: Tenant, action: ServerAction, userToken: UserToken, siteArea: SiteArea,
- filteredRequest: HttpAssignChargingStationToSiteAreaRequest): Promise<AuthorizationFilter> {
+ tenant: Tenant, action: ServerAction, userToken: UserToken, filteredRequest: Record<string, any>): Promise<AuthorizationFilter> {
const authorizationFilters: AuthorizationFilter = {
filters: {},
dataSources: new Map(),
projectFields: [],
- authorized: userToken.role === UserRole.ADMIN,
+ authorized: false,
};
- // Not an Admin?
- if (userToken.role !== UserRole.ADMIN) {
- // Get Site IDs for which user is admin from db
- const siteAdminSiteIDs = await AuthorizationService.getSiteAdminSiteIDs(tenant.id, userToken);
- // Check Site
- if (!Utils.isEmptyArray(siteAdminSiteIDs) && siteAdminSiteIDs.includes(siteArea.siteID)) {
- // Site Authorized, now check Assets
- if (!Utils.isEmptyArray(filteredRequest.chargingStationIDs)) {
- let foundInvalidChargingStationID = false;
- // Get Charging Station IDs already assigned to the Site
- const chargingStationIDs = await AuthorizationService.getAssignedChargingStationIDs(tenant.id, siteArea.siteID);
- // Check if any of the Charging Stations we want to unassign are missing
- for (const chargingStationID of filteredRequest.chargingStationIDs) {
- switch (action) {
- case ServerAction.ADD_CHARGING_STATIONS_TO_SITE_AREA:
- if (chargingStationIDs.includes(chargingStationID)) {
- foundInvalidChargingStationID = true;
- }
- break;
- case ServerAction.REMOVE_CHARGING_STATIONS_FROM_SITE_AREA:
- if (!chargingStationIDs.includes(chargingStationID)) {
- foundInvalidChargingStationID = true;
- }
- break;
- }
- }
- if (!foundInvalidChargingStationID) {
- authorizationFilters.authorized = true;
- }
- }
- }
+ // Check static auth
+ const authorizationContext: AuthorizationContext = {};
+ const authAction = action === ServerAction.ADD_CHARGING_STATIONS_TO_SITE_AREA ? Action.ASSIGN_CHARGING_STATIONS : Action.UNASSIGN_CHARGING_STATIONS;
+ const authResult = authAction === Action.ASSIGN_CHARGING_STATIONS ?
+ await Authorizations.canAssignSiteAreaChargingStations(userToken, authorizationContext) :
+ await Authorizations.canUnassignSiteAreaChargingStations(userToken, authorizationContext);
+ authorizationFilters.authorized = authResult.authorized;
+ // Check
+ if (!authorizationFilters.authorized) {
+ throw new AppAuthError({
+ errorCode: HTTPAuthError.FORBIDDEN,
+ user: userToken,
+ action: authAction,
+ entity: Entity.SITE_AREA,
+ module: MODULE_NAME, method: 'checkAssignSiteAreaChargingStationsAuthorizationFilters',
+ });
}
+ // Process dynamic filters
+ await AuthorizationService.processDynamicFilters(tenant, userToken, authAction, Entity.SITE_AREA,
+ authorizationFilters, authorizationContext, { SiteAreaID: filteredRequest.siteAreaID });
+ // Filter projected fields
+ authorizationFilters.projectFields = AuthorizationService.filterProjectFields(
+ authResult.fields, filteredRequest.ProjectFields);
return authorizationFilters;
}
@@ -594,6 +588,14 @@ export default class AuthorizationService {
siteArea.canRead = await AuthorizationService.canPerformAuthorizationAction(tenant, userToken, Entity.SITE_AREA, Action.READ, authorizationFilter, filteredRequest);
siteArea.canUpdate = await AuthorizationService.canPerformAuthorizationAction(tenant, userToken, Entity.SITE_AREA, Action.DELETE, authorizationFilter, filteredRequest);
siteArea.canDelete = await AuthorizationService.canPerformAuthorizationAction(tenant, userToken, Entity.SITE_AREA, Action.UPDATE, authorizationFilter, filteredRequest);
+ siteArea.canAssignAssets = await AuthorizationService.canPerformAuthorizationAction(tenant, userToken, Entity.SITE_AREA,
+ Action.ASSIGN_ASSETS, authorizationFilter, filteredRequest);
+ siteArea.canUnassignAssets = await AuthorizationService.canPerformAuthorizationAction(tenant, userToken, Entity.SITE_AREA,
+ Action.UNASSIGN_ASSETS, authorizationFilter, filteredRequest);
+ siteArea.canAssignChargingStations = await AuthorizationService.canPerformAuthorizationAction(tenant, userToken, Entity.SITE_AREA,
+ Action.ASSIGN_CHARGING_STATIONS, authorizationFilter, filteredRequest);
+ siteArea.canUnassignChargingStations = await AuthorizationService.canPerformAuthorizationAction(tenant, userToken, Entity.SITE_AREA,
+ Action.UNASSIGN_CHARGING_STATIONS, authorizationFilter, filteredRequest);
}
}
@@ -624,6 +626,20 @@ export default class AuthorizationService {
return authorizationFilters;
}
+ public static async checkAndGetChargingStationsAuthorizationFilters(tenant: Tenant, userToken: UserToken,
+ filteredRequest: Record<string,any>):Promise<AuthorizationFilter> {
+ const authorizationFilters: AuthorizationFilter = {
+ filters: {},
+ dataSources: new Map(),
+ projectFields: [],
+ authorized: false,
+ };
+ // Check static & dynamic authorization
+ await this.canPerformAuthorizationAction(
+ tenant, userToken, Entity.CHARGING_STATIONS, Action.LIST, authorizationFilters, filteredRequest);
+ return authorizationFilters;
+ }
+
public static async getSiteAdminSiteIDs(tenantID: string, userToken: UserToken): Promise<string[]> {
// Get the Sites where the user is Site Admin
const userSites = await UserStorage.getUserSites(tenantID,

MODIFY
src/server/rest/v1/service/SiteAreaService.ts
src/server/rest/v1/service/SiteAreaService.ts
@@ -76,21 +76,9 @@ export default class SiteAreaService {
Action.UPDATE, Entity.CHARGING_STATION, MODULE_NAME, 'handleAssignChargingStationsToSiteArea');
// Filter request
const filteredRequest = SiteAreaSecurity.filterAssignChargingStationsToSiteAreaRequest(req.body);
- // Check mandatory fields
- UtilsService.assertIdIsProvided(action, filteredRequest.siteAreaID, MODULE_NAME, 'handleAssignChargingStationsToSiteArea', req.user);
- if (Utils.isEmptyArray(filteredRequest.chargingStationIDs)) {
- throw new AppError({
- source: Constants.CENTRAL_SERVER,
- errorCode: HTTPError.GENERAL_ERROR,
- message: 'The Charging Station\'s IDs must be provided',
- module: MODULE_NAME,
- method: 'handleAssignChargingStationsToSiteArea',
- user: req.user
- });
- }
// Check and Get Site Area
const siteArea = await UtilsService.checkAndGetSiteAreaAuthorization(
- req.tenant, req.user, filteredRequest.siteAreaID, Action.UPDATE, action, {});
+ req.tenant, req.user, filteredRequest.siteAreaID, Action.READ, action, {}); // TODO: CHECK IF WE SHOULD HAVE ASSIGN/UNASSIGN action instead of READ
// Check and Get Charging Stations
const chargingStations = await UtilsService.checkSiteAreaChargingStationsAuthorization(
req.tenant, req.user, siteArea, filteredRequest.chargingStationIDs, action, {});

MODIFY
src/server/rest/v1/service/UtilsService.ts
src/server/rest/v1/service/UtilsService.ts
@@ -101,6 +101,44 @@ export default class UtilsService {
return chargingStation;
}
+ public static async checkAndGetChargingStationsAuthorization(tenant: Tenant, userToken: UserToken, action: ServerAction,
+ additionalFilters: Record<string, any>, applyProjectFields = false): Promise<ChargingStation[]> {
+ // Check dynamic auth for assignment
+ const authorizationFilter = await AuthorizationService.checkAndGetChargingStationsAuthorizationFilters(tenant, userToken, {});
+ if (!authorizationFilter.authorized) {
+ throw new AppAuthError({
+ errorCode: HTTPAuthError.FORBIDDEN,
+ user: userToken,
+ action: Action.LIST,
+ entity: Entity.CHARGING_STATIONS,
+ module: MODULE_NAME, method: 'checkAndGetChargingStationsAuthorization',
+ });
+ }
+ // Get Charging Stations
+ const chargingStations = await ChargingStationStorage.getChargingStations(tenant.id,
+ {
+ ...additionalFilters,
+ ...authorizationFilter.filters,
+ }, Constants.DB_PARAMS_MAX_LIMIT,
+ applyProjectFields ? authorizationFilter.projectFields : null
+ );
+ // Check
+ for (const chargingStation of chargingStations.result) {
+ // External Charging Station
+ if (!chargingStation.issuer) {
+ throw new AppError({
+ source: Constants.CENTRAL_SERVER,
+ errorCode: HTTPError.GENERAL_ERROR,
+ message: `Charging Station ID '${chargingStation.id}' not issued by the organization`,
+ module: MODULE_NAME, method: 'checkAndGetChargingStationsAuthorization',
+ user: userToken,
+ action: action
+ });
+ }
+ }
+ return chargingStations.result;
+ }
+
public static async checkAndGetCompanyAuthorization(tenant: Tenant, userToken: UserToken, companyID: string, authAction: Action,
action: ServerAction, additionalFilters: Record<string, any>, applyProjectFields = false): Promise<Company> {
// Check mandatory fields
@@ -380,29 +418,22 @@ export default class UtilsService {
public static async checkSiteAreaChargingStationsAuthorization(tenant: Tenant, userToken: UserToken, siteArea: SiteArea, chargingStationIDs: string[],
action: ServerAction, additionalFilters: Record<string, any>, applyProjectFields = false): Promise<ChargingStation[]> {
- // Check dynamic auth for assignment
- const authorizationFilter = await AuthorizationService.checkAssignSiteAreaChargingStationsAuthorizationFilters(
- tenant, action, userToken, siteArea, { siteAreaID: siteArea.id, chargingStationIDs });
- if (!authorizationFilter.authorized) {
- throw new AppAuthError({
- errorCode: HTTPAuthError.FORBIDDEN,
- user: userToken,
- action: action === ServerAction.ADD_CHARGING_STATIONS_TO_SITE_AREA ? Action.ASSIGN : Action.UNASSIGN,
- entity: Entity.CHARGING_STATION,
- module: MODULE_NAME, method: 'checkSiteAreaChargingStationsAuthorization',
+ // Check mandatory fields
+ UtilsService.assertIdIsProvided(action, siteArea.id, MODULE_NAME, 'checkSiteAreaChargingStationsAuthorization', userToken);
+ if (Utils.isEmptyArray(chargingStationIDs)) {
+ throw new AppError({
+ source: Constants.CENTRAL_SERVER,
+ errorCode: HTTPError.GENERAL_ERROR,
+ message: 'The Charging Station\'s IDs must be provided',
+ module: MODULE_NAME,
+ method: 'checkSiteAreaChargingStationsAuthorization',
+ user: userToken
});
}
- // Get Charging Stations
- const chargingStations = await ChargingStationStorage.getChargingStations(tenant.id,
- {
- chargingStationIDs,
- ...additionalFilters,
- ...authorizationFilter.filters,
- }, Constants.DB_PARAMS_MAX_LIMIT,
- applyProjectFields ? authorizationFilter.projectFields : null
- );
+ const chargingStations = await this.checkAndGetChargingStationsAuthorization(tenant, userToken, action,
+ { chargingStationIDs, ...additionalFilters }, applyProjectFields);
// Must have the same result
- if (chargingStationIDs.length !== chargingStations.result.length) {
+ if (chargingStationIDs.length !== chargingStations.length) {
throw new AppAuthError({
errorCode: HTTPAuthError.FORBIDDEN,
user: userToken,
@@ -411,21 +442,7 @@ export default class UtilsService {
module: MODULE_NAME, method: 'checkSiteAreaChargingStationsAuthorization',
});
}
- // Check
- for (const chargingStation of chargingStations.result) {
- // External Charging Station
- if (!chargingStation.issuer) {
- throw new AppError({
- source: Constants.CENTRAL_SERVER,
- errorCode: HTTPError.GENERAL_ERROR,
- message: `Charging Station ID '${chargingStation.id}' not issued by the organization`,
- module: MODULE_NAME, method: 'checkSiteAreaChargingStationsAuthorization',
- user: userToken,
- action: action
- });
- }
- }
- return chargingStations.result;
+ return chargingStations;
}
public static async checkAndGetSiteAreaAuthorization(tenant: Tenant, userToken: UserToken, siteAreaID: string, authAction: Action,

MODIFY
src/types/Authorization.ts
src/types/Authorization.ts
@@ -178,6 +178,12 @@ export interface AuthorizationActions {
canUpdate?: boolean;
canDelete?: boolean;
}
+export interface SiteAreaAuthorizationActions extends AuthorizationActions {
+ canAssignAssets?: boolean;
+ canUnassignAssets?: boolean;
+ canAssignChargingStations?: boolean;
+ canUnassignChargingStations?: boolean;
+}
export enum DynamicAuthorizationFilterName {
ASSIGNED_SITES_COMPANIES = 'AssignedSitesCompanies',

MODIFY
src/types/SiteArea.ts
src/types/SiteArea.ts
import ChargingStation, { Voltage } from '../types/ChargingStation';
import Address from './Address';
-import { AuthorizationActions } from './Authorization';
import ConnectorStats from './ConnectorStats';
import Consumption from './Consumption';
import CreatedUpdatedProps from './CreatedUpdatedProps';
import Site from '../types/Site';
+import { SiteAreaAuthorizationActions } from './Authorization';
-export default interface SiteArea extends CreatedUpdatedProps, AuthorizationActions {
+export default interface SiteArea extends CreatedUpdatedProps, SiteAreaAuthorizationActions {
id: string;
name: string;
issuer: boolean;

Change assignChargingStationsToSiteArea logic.