pricing - authorization - proposal for contextual permissions
MODIFY
src/server/rest/v1/service/AuthorizationService.ts
src/server/rest/v1/service/AuthorizationService.ts
import { Action, AuthorizationActions, AuthorizationContext, AuthorizationFilter, Entity, SiteAreaAuthorizationActions, TagAuthorizationActions } from '../../../../types/Authorization';
import { Car, CarCatalog } from '../../../../types/Car';
-import { CarCatalogDataResult, CarDataResult, CompanyDataResult, LogDataResult, PricingDataResult, SiteAreaDataResult, SiteDataResult, TagDataResult, UserDataResult } from '../../../../types/DataResult';
+import { CarCatalogDataResult, CarDataResult, CompanyDataResult, LogDataResult, PricingDefinitionDataResult, SiteAreaDataResult, SiteDataResult, TagDataResult, UserDataResult } from '../../../../types/DataResult';
import { HttpCarCatalogRequest, HttpCarCatalogsRequest, HttpCarRequest, HttpCarsRequest } from '../../../../types/requests/HttpCarRequest';
import { HttpChargingStationRequest, HttpChargingStationsRequest } from '../../../../types/requests/HttpChargingStationRequest';
import { HttpCompaniesRequest, HttpCompanyRequest } from '../../../../types/requests/HttpCompanyRequest';
@@ -412,7 +412,7 @@ export default class AuthorizationService {
return authorizationFilters;
}
- public static async addPricingDefinitionsAuthorizations(tenant: Tenant, userToken: UserToken, dataResult: PricingDataResult, filter: AuthorizationFilter): Promise<void> {
+ public static async addPricingDefinitionsAuthorizations(tenant: Tenant, userToken: UserToken, dataResult: PricingDefinitionDataResult, filter: AuthorizationFilter): Promise<void> {
// Add canCreate flag to root
dataResult.canCreate = await AuthorizationService.canPerformAuthorizationAction(
tenant, userToken, Entity.PRICING_DEFINITION, Action.CREATE, filter);

MODIFY
src/server/rest/v1/service/PricingService.ts
src/server/rest/v1/service/PricingService.ts
@@ -8,7 +8,7 @@ import ChargingStation from '../../../../types/ChargingStation';
import Constants from '../../../../utils/Constants';
import { HTTPAuthError } from '../../../../types/HTTPError';
import Logging from '../../../../utils/Logging';
-import { PricingDataResult } from '../../../../types/DataResult';
+import { PricingDefinitionDataResult } from '../../../../types/DataResult';
import PricingStorage from '../../../../storage/mongodb/PricingStorage';
import PricingValidator from '../validator/PricingValidator';
import { ServerAction } from '../../../../types/Server';
@@ -62,18 +62,34 @@ export default class PricingService {
onlyRecordCount: filteredRequest.OnlyRecordCount
},
authorizationPricingDefinitionsFilter.projectFields
- );
+ ) as PricingDefinitionDataResult;
// Assign projected fields
if (authorizationPricingDefinitionsFilter.projectFields) {
pricingDefinitions.projectFields = authorizationPricingDefinitionsFilter.projectFields;
}
// Add Auth flags
- await AuthorizationService.addPricingDefinitionsAuthorizations(req.tenant, req.user, pricingDefinitions as PricingDataResult, authorizationPricingDefinitionsFilter);
+ await AuthorizationService.addPricingDefinitionsAuthorizations(req.tenant, req.user, pricingDefinitions , authorizationPricingDefinitionsFilter);
+ // Alter the canCreate flag according to the pricing definition context
+ pricingDefinitions.canCreate = await PricingService.alterCanCreate(req, action, filteredRequest.EntityType, filteredRequest.EntityID, pricingDefinitions.canCreate);
// Return
res.json(pricingDefinitions);
next();
}
+ public static async alterCanCreate(req: Request, action: ServerAction, entityType: PricingEntity, entityID: string, canCreate: boolean): Promise<boolean> {
+ try {
+ // Get the site ID for the current entity
+ const siteID = await PricingService.checkAuthorizationAndGetSiteID(req, action, entityType, entityID);
+ if (siteID) {
+ // TODO: To be clarified - Here we reuse the Site UPDATE permission to determine whether we can create a pricing definition or not
+ await UtilsService.checkAndGetSiteAuthorization(req.tenant, req.user, siteID, Action.UPDATE, action);
+ }
+ } catch (e) {
+ canCreate = false;
+ }
+ return canCreate;
+ }
+
public static async handleCreatePricingDefinition(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
// Check if component is active
UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.PRICING,
@@ -94,7 +110,7 @@ export default class PricingService {
});
}
// Check authorization and get the site ID depending on the entity type
- const siteID = await PricingService.checkAuthorizationAndGetSiteID(req, action, filteredRequest);
+ const siteID = await PricingService.checkAuthorizationAndGetSiteID(req, action, filteredRequest.entityType, filteredRequest.entityID);
// Create pricing
const newPricingDefinition: PricingDefinition = {
...filteredRequest,
@@ -130,7 +146,7 @@ export default class PricingService {
const pricingDefinition = await UtilsService.checkAndGetPricingDefinitionAuthorization(
req.tenant, req.user, filteredRequest.id, Action.UPDATE, action, filteredRequest);
// Check authorization and get the site ID depending on the entity type
- const siteID = await PricingService.checkAuthorizationAndGetSiteID(req, action, filteredRequest);
+ const siteID = await PricingService.checkAuthorizationAndGetSiteID(req, action, filteredRequest.entityType, filteredRequest.entityID);
// Update
pricingDefinition.entityID = filteredRequest.entityID;
pricingDefinition.entityType = filteredRequest.entityType;
@@ -181,24 +197,24 @@ export default class PricingService {
next();
}
- private static async checkAuthorizationAndGetSiteID(req: Request, action: ServerAction, filteredRequest: PricingDefinition): Promise<string> {
- let siteID;
+ private static async checkAuthorizationAndGetSiteID(req: Request, action: ServerAction, entityType: PricingEntity, entityID: string): Promise<string> {
+ let siteID: string;
let site: Site, siteArea: SiteArea, chargingStation: ChargingStation;
- switch (filteredRequest.entityType) {
+ switch (entityType) {
case PricingEntity.COMPANY:
- await UtilsService.checkAndGetCompanyAuthorization(req.tenant, req.user, filteredRequest.entityID, Action.READ, action);
+ await UtilsService.checkAndGetCompanyAuthorization(req.tenant, req.user, entityID, Action.READ, action);
siteID = null;
break;
case PricingEntity.SITE:
- site = await UtilsService.checkAndGetSiteAuthorization(req.tenant, req.user, filteredRequest.entityID, Action.READ, action);
+ site = await UtilsService.checkAndGetSiteAuthorization(req.tenant, req.user, entityID, Action.READ, action);
siteID = site.id;
break;
case PricingEntity.SITE_AREA:
- siteArea = await UtilsService.checkAndGetSiteAreaAuthorization(req.tenant, req.user, filteredRequest.entityID, Action.READ, action);
+ siteArea = await UtilsService.checkAndGetSiteAreaAuthorization(req.tenant, req.user, entityID, Action.READ, action);
siteID = siteArea.siteID;
break;
case PricingEntity.CHARGING_STATION:
- chargingStation = await UtilsService.checkAndGetChargingStationAuthorization(req.tenant, req.user, filteredRequest.entityID, action);
+ chargingStation = await UtilsService.checkAndGetChargingStationAuthorization(req.tenant, req.user, entityID, action);
siteID = chargingStation.siteID;
break;
default:
@@ -207,3 +223,4 @@ export default class PricingService {
return siteID;
}
}
+

MODIFY
src/server/rest/v1/validator/PricingValidator.ts
src/server/rest/v1/validator/PricingValidator.ts
@@ -27,11 +27,11 @@ export default class PricingValidator extends SchemaValidator {
return PricingValidator.instance;
}
- public validatePricingDefinitionGet(data: Record<string, unknown>): Partial<HttpPricingDefinitionsRequest> {
+ public validatePricingDefinitionGet(data: Record<string, unknown>): HttpPricingDefinitionsRequest {
return this.validate(this.pricingDefinitionGet, data);
}
- public validatePricingDefinitionsGet(data: Record<string, unknown>): Partial<HttpPricingDefinitionsRequest> {
+ public validatePricingDefinitionsGet(data: Record<string, unknown>): HttpPricingDefinitionsRequest {
return this.validate(this.pricingDefinitionsGet, data);
}

MODIFY
src/storage/mongodb/PricingStorage.ts
src/storage/mongodb/PricingStorage.ts
@@ -64,7 +64,7 @@ export default class PricingStorage {
}
public static async getPricingDefinition(tenant: Tenant, id: string,
- params: { entityID?: string; entityType?: string; withEntityInformation?: boolean; siteIDs?: string[];} = {}, projectFields?: string[]): Promise<PricingDefinition> {
+ params: { entityID?: string; entityType?: PricingEntity; withEntityInformation?: boolean; siteIDs?: string[];} = {}, projectFields?: string[]): Promise<PricingDefinition> {
const pricingDefinitionMDB = await PricingStorage.getPricingDefinitions(tenant, {
siteIDs: params.siteIDs,
pricingDefinitionIDs: [id],
@@ -76,7 +76,7 @@ export default class PricingStorage {
}
public static async getPricingDefinitions(tenant: Tenant,
- params: { pricingDefinitionIDs?: string[], entityType?: string; entityID?: string; siteIDs?: string[]; withEntityInformation?: boolean; },
+ params: { pricingDefinitionIDs?: string[], entityType?: PricingEntity; entityID?: string; siteIDs?: string[]; withEntityInformation?: boolean; },
dbParams: DbParams, projectFields?: string[]): Promise<DataResult<PricingDefinition>> {
const uniqueTimerID = Logging.traceDatabaseRequestStart();
// Check Tenant

MODIFY
src/types/DataResult.ts
src/types/DataResult.ts
@@ -22,7 +22,7 @@ export interface DataResult<T> {
metadata?: Record<string, AuthorizationDefinitionFieldMetadata>;
}
-export interface PricingDataResult extends DataResult<PricingDefinition>{
+export interface PricingDefinitionDataResult extends DataResult<PricingDefinition>{
canCreate: boolean;
}

MODIFY
src/types/requests/HttpPricingRequest.ts
src/types/requests/HttpPricingRequest.ts
import HttpByIDRequest from './HttpByIDRequest';
import HttpDatabaseRequest from './HttpDatabaseRequest';
+import { PricingEntity } from '../Pricing';
export interface HttpPricingDefinitionRequest extends HttpByIDRequest {
ID: string;
@@ -10,6 +11,6 @@ export interface HttpPricingDefinitionsRequest extends HttpDatabaseRequest {
ID?: string;
Search?: string;
EntityID?: string;
- EntityType?: string;
+ EntityType?: PricingEntity;
WithEntityInformation?: boolean;
}

pricing - authorization - proposal for contextual permissions