Billing setting migration
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, BillingInvoiceDataResult, BillingPaymentMethodDataResult, CarCatalogDataResult, CarDataResult, CompanyDataResult, LogDataResult, PricingDefinitionDataResult, RegistrationTokenDataResult, SiteAreaDataResult, SiteDataResult, TagDataResult, UserDataResult } from '../../../../types/DataResult';
+import { AssetDataResult, BillingInvoiceDataResult, BillingPaymentMethodDataResult, CarCatalogDataResult, CarDataResult, CompanyDataResult, DataResult, LogDataResult, PricingDefinitionDataResult, RegistrationTokenDataResult, SiteAreaDataResult, SiteDataResult, TagDataResult, UserDataResult } from '../../../../types/DataResult';
import { BillingInvoice, BillingPaymentMethod } from '../../../../types/Billing';
import { Car, CarCatalog } from '../../../../types/Car';
import { HttpAssetRequest, HttpAssetsRequest } from '../../../../types/requests/HttpAssetRequest';
@@ -30,6 +30,7 @@ import Logging from '../../../../utils/Logging';
import PricingDefinition from '../../../../types/Pricing';
import RegistrationToken from '../../../../types/RegistrationToken';
import { ServerAction } from '../../../../types/Server';
+import { Setting } from '../../../../types/Setting';
import Site from '../../../../types/Site';
import SiteArea from '../../../../types/SiteArea';
import Tag from '../../../../types/Tag';
@@ -574,8 +575,7 @@ export default class AuthorizationService {
// Billing
public static async checkAndGetBillingAuthorizations(tenant: Tenant, userToken: UserToken, authAction: Action, entityData?: EntityData): Promise<AuthorizationFilter> {
- return AuthorizationService.checkAndGetEntityAuthorizations(
- tenant, Entity.BILLING, userToken, {}, {}, authAction, entityData);
+ return AuthorizationService.checkAndGetEntityAuthorizations(tenant, Entity.BILLING, userToken, {}, {}, authAction, entityData);
}
// End billing
@@ -651,8 +651,7 @@ export default class AuthorizationService {
public static async checkAndGetPaymentMethodAuthorizations(tenant: Tenant, userToken: UserToken,
filteredRequest: Partial<HttpSetupPaymentMethod | HttpDeletePaymentMethod>, authAction: Action, entityData?: EntityData): Promise<AuthorizationFilter> {
return AuthorizationService.checkAndGetEntityAuthorizations(
- tenant, Entity.PAYMENT_METHOD, userToken, filteredRequest, filteredRequest.userID ? { UserID: filteredRequest.userID } : {},
- authAction, entityData);
+ tenant, Entity.PAYMENT_METHOD, userToken, filteredRequest, filteredRequest.userID ? { UserID: filteredRequest.userID } : {}, authAction, entityData);
}
// SPECIAL CASE: to apply dynamic filters we need the filtered request as it contains the userID (can be different from the userToken)
@@ -680,6 +679,42 @@ export default class AuthorizationService {
// end payment
+ // setting
+ public static async checkAndGetSettingsAuthorizations(tenant: Tenant, userToken: UserToken): Promise<AuthorizationFilter> {
+ const authorizationFilters: AuthorizationFilter = {
+ filters: {},
+ dataSources: new Map(),
+ projectFields: [],
+ authorized: false
+ };
+ // Check static & dynamic authorization
+ await AuthorizationService.canPerformAuthorizationAction(tenant, userToken, Entity.SETTING, Action.LIST, authorizationFilters, null, null, true);
+ return authorizationFilters;
+ }
+
+ public static async checkAndGetSettingAuthorizations(tenant: Tenant, userToken: UserToken, authAction: Action, entityData?: EntityData): Promise<AuthorizationFilter> {
+ return AuthorizationService.checkAndGetEntityAuthorizations(
+ tenant, Entity.SETTING, userToken, {}, null, authAction, entityData);
+ }
+
+ public static async addSettingsAuthorizations(tenant: Tenant, userToken: UserToken, settings: DataResult<Setting>,
+ authorizationFilter: AuthorizationFilter): Promise<void> {
+ // Add Meta Data
+ settings.metadata = authorizationFilter.metadata;
+ for (const setting of settings.result) {
+ await AuthorizationService.addSettingAuthorizations(tenant, userToken, setting, authorizationFilter);
+ }
+ }
+
+ public static async addSettingAuthorizations(tenant: Tenant, userToken: UserToken, setting: Setting, authorizationFilter: AuthorizationFilter): Promise<void> {
+ setting.canRead = true; // Always true as it should be filtered upfront
+ setting.canUpdate = await AuthorizationService.canPerformAuthorizationAction(
+ tenant, userToken, Entity.SETTING, Action.UPDATE, authorizationFilter);
+ // Remove auth flags set to false
+ Utils.removeCanPropertiesWithFalseValue(setting);
+ }
+ // end setting
+
public static async checkAndGetCarsAuthorizations(tenant: Tenant, userToken: UserToken, filteredRequest: Partial<HttpCarsRequest>): Promise<AuthorizationFilter> {
const authorizationFilters: AuthorizationFilter = {
filters: {},

MODIFY
src/server/rest/v1/service/BillingService.ts
src/server/rest/v1/service/BillingService.ts
@@ -401,17 +401,8 @@ export default class BillingService {
// Check if component is active
UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.BILLING,
Action.READ, Entity.SETTING, MODULE_NAME, 'handleGetBillingSetting');
- // Check auth
- if (!await Authorizations.canReadBillingSetting(req.user)) {
- throw new AppAuthError({
- errorCode: HTTPAuthError.FORBIDDEN,
- user: req.user,
- action: Action.READ, entity: Entity.SETTING,
- module: MODULE_NAME, method: 'handleGetBillingSetting',
- });
- }
- const billingSettings: BillingSettings = await SettingStorage.getBillingSetting(req.tenant);
- UtilsService.assertObjectExists(action, billingSettings, 'Failed to load billing settings', MODULE_NAME, 'handleGetBillingSetting', req.user);
+ const billingSettings: BillingSettings = await UtilsService.checkAndGetBillingSettingAuthorization(req.tenant, req.user, null, Action.READ, action);
+ // Process sensitive data
UtilsService.hashSensitiveData(req.user.tenantID, billingSettings);
res.json(billingSettings);
next();
@@ -421,19 +412,10 @@ export default class BillingService {
// Check if component is active
UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.BILLING,
Action.UPDATE, Entity.SETTING, MODULE_NAME, 'handleUpdateBillingSetting');
- // Check auth
- if (!await Authorizations.canUpdateBillingSetting(req.user)) {
- throw new AppAuthError({
- errorCode: HTTPAuthError.FORBIDDEN,
- user: req.user,
- action: Action.UPDATE, entity: Entity.SETTING,
- module: MODULE_NAME, method: 'handleUpdateBillingSetting',
- });
- }
const newBillingProperties = BillingValidator.getInstance().validateBillingSettingUpdateReq({ ...req.params, ...req.body });
- // Load previous settings
- const billingSettings = await SettingStorage.getBillingSetting(req.tenant);
- UtilsService.assertObjectExists(action, billingSettings, 'Failed to load billing settings', MODULE_NAME, 'handleUpdateBillingSetting', req.user);
+ // Check and get previous settings that we want to update
+ const billingSettings: BillingSettings = await UtilsService.checkAndGetBillingSettingAuthorization(req.tenant, req.user, null, Action.UPDATE, action);
+ // Process sensitive data
await UtilsService.processSensitiveData(req.tenant, billingSettings, newBillingProperties);
// Billing properties to preserve
const { usersLastSynchronizedOn } = billingSettings.billing;

MODIFY
src/server/rest/v1/service/UtilsService.ts
src/server/rest/v1/service/UtilsService.ts
@@ -14,6 +14,7 @@ import AuthorizationService from './AuthorizationService';
import Authorizations from '../../../../authorization/Authorizations';
import AxiosFactory from '../../../../utils/AxiosFactory';
import { BillingInvoice } from '../../../../types/Billing';
+import { BillingSettings } from '../../../../types/Setting';
import BillingStorage from '../../../../storage/mongodb/BillingStorage';
import CarStorage from '../../../../storage/mongodb/CarStorage';
import CentralSystemRestServiceConfiguration from '../../../../types/configuration/CentralSystemRestServiceConfiguration';
@@ -37,6 +38,7 @@ import PricingStorage from '../../../../storage/mongodb/PricingStorage';
import RegistrationToken from '../../../../types/RegistrationToken';
import RegistrationTokenStorage from '../../../../storage/mongodb/RegistrationTokenStorage';
import { ServerAction } from '../../../../types/Server';
+import SettingStorage from '../../../../storage/mongodb/SettingStorage';
import Site from '../../../../types/Site';
import SiteArea from '../../../../types/SiteArea';
import SiteAreaStorage from '../../../../storage/mongodb/SiteAreaStorage';
@@ -847,6 +849,49 @@ export default class UtilsService {
return carCatalog;
}
+ // This function is tailored for SETTING authorization, do not use it for "general" entities !
+ public static async checkAndGetBillingSettingAuthorization(tenant: Tenant, userToken: UserToken, billingSettingID: number, authAction: Action,
+ action: ServerAction, entityData?: EntityData, additionalFilters: Record<string, any> = {}, applyProjectFields = false): Promise<BillingSettings> {
+ // Get dynamic auth
+ const authorizationFilter = await AuthorizationService.checkAndGetSettingAuthorizations(tenant, userToken, authAction, entityData);
+ if (!authorizationFilter.authorized) {
+ throw new AppAuthError({
+ errorCode: HTTPAuthError.FORBIDDEN,
+ user: userToken,
+ action: authAction, entity: Entity.SETTING,
+ module: MODULE_NAME, method: 'checkAndGetBillingSettingAuthorization',
+ });
+ }
+ // Get the entity from storage
+ const billingSetting = await SettingStorage.getBillingSetting(
+ tenant,
+ applyProjectFields ? authorizationFilter.projectFields : null
+ );
+ // Check it exists
+ UtilsService.assertObjectExists(action, billingSetting, `Billing setting for tenantID '${tenant.id}' does not exist`,
+ MODULE_NAME, 'checkAndGetBillingSettingAuthorization', userToken);
+ // Assign projected fields
+ if (applyProjectFields && authorizationFilter.projectFields) {
+ billingSetting.projectFields = authorizationFilter.projectFields;
+ }
+ // Assign Metadata
+ if (authorizationFilter.metadata) {
+ billingSetting.metadata = authorizationFilter.metadata;
+ }
+ // Add actions
+ await AuthorizationService.addSettingAuthorizations(tenant, userToken, billingSetting, authorizationFilter);
+ const authorized = AuthorizationService.canPerformAction(billingSetting, authAction);
+ if (!authorized) {
+ throw new AppAuthError({
+ errorCode: HTTPAuthError.FORBIDDEN,
+ user: userToken,
+ action: authAction, entity: Entity.SETTING,
+ module: MODULE_NAME, method: 'checkAndGetBillingSettingAuthorization',
+ });
+ }
+ return billingSetting;
+ }
+
public static async checkAndGetInvoiceAuthorization(tenant: Tenant, userToken: UserToken, invoiceID: string, authAction: Action,
action: ServerAction, entityData?: EntityData, additionalFilters: Record<string, any> = {}, additionalProjectedFields: string[] = [],
applyProjectFields = false): Promise<BillingInvoice> {

MODIFY
src/storage/mongodb/SettingStorage.ts
src/storage/mongodb/SettingStorage.ts
@@ -446,9 +446,9 @@ export default class SettingStorage {
await SettingStorage.saveSettings(tenant, settingsToSave);
}
- public static async getBillingSetting(tenant: Tenant): Promise<BillingSettings> {
+ public static async getBillingSetting(tenant: Tenant, projectedFields?: string[]): Promise<BillingSettings> {
// Get BILLING Settings by Identifier
- const setting = await SettingStorage.getSettingByIdentifier(tenant, TenantComponents.BILLING);
+ const setting = await SettingStorage.getSettingByIdentifier(tenant, TenantComponents.BILLING, projectedFields);
if (setting) {
const { id, backupSensitiveData, category } = setting;
const { createdBy, createdOn, lastChangedBy, lastChangedOn } = setting;

MODIFY
src/types/GlobalType.ts
src/types/GlobalType.ts
@@ -9,6 +9,7 @@ import MongoDBStorage from '../storage/mongodb/MongoDBStorage';
import PricingDefinition from './Pricing';
import RegistrationToken from './RegistrationToken';
import { ServerType } from './Server';
+import { Setting } from './Setting';
import Site from './Site';
import SiteArea from './SiteArea';
import SoapCentralSystemServer from '../server/ocpp/soap/SoapCentralSystemServer';
@@ -75,7 +76,7 @@ export enum ImportStatus {
ERROR = 'E',
}
-export type EntityData = Car|User|Company|Site|SiteArea|Tag|CarCatalog|ChargingStation|PricingDefinition|Log|RegistrationToken|BillingInvoice|BillingPaymentMethod;
+export type EntityData = Car|User|Company|Site|SiteArea|Tag|CarCatalog|ChargingStation|PricingDefinition|Log|RegistrationToken|BillingInvoice|BillingPaymentMethod|Setting;
interface TSGlobal extends Global {
database: MongoDBStorage;

MODIFY
src/types/Setting.ts
src/types/Setting.ts
+import { AuthorizationActions } from './Authorization';
import CreatedUpdatedProps from './CreatedUpdatedProps';
import { TenantComponents } from './Tenant';
@@ -21,7 +22,7 @@ export enum IntegrationSettings {
STATISTICS = 'statistics'
}
-export interface Setting extends CreatedUpdatedProps {
+export interface Setting extends AuthorizationActions, CreatedUpdatedProps {
id?: string;
identifier: TenantComponents | TechnicalSettings;
sensitiveData?: string[];

Billing setting migration