Add auth for LIST/READ
MODIFY
src/authorization/AuthorizationsDefinition.ts
src/authorization/AuthorizationsDefinition.ts
@@ -426,11 +426,46 @@ export const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
}
},
},
- { resource: Entity.TRANSACTION, action: [Action.LIST, Action.EXPORT, Action.IN_ERROR] },
+ {
+ resource: Entity.TRANSACTION, action: Action.LIST,
+ condition: {
+ Fn: 'custom:dynamicAuthorizations',
+ args: {
+ asserts: [],
+ filters: []
+ }
+ },
+ attributes: [
+ 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', 'companyID',
+ 'currentTotalDurationSecs', 'currentTotalInactivitySecs', 'currentInstantWatts', 'currentTotalConsumptionWh', 'currentStateOfCharge',
+ 'currentCumulatedPrice', 'currentInactivityStatus', 'roundedPrice', 'price', 'priceUnit', 'ocpi', 'ocpiWithCdr',
+ 'stop.roundedPrice', 'stop.price', 'stop.priceUnit', 'stop.inactivityStatus', 'stop.stateOfCharge', 'stop.timestamp', 'stop.totalConsumptionWh',
+ 'stop.totalDurationSecs', 'stop.totalInactivitySecs', 'stop.extraInactivitySecs', 'stop.meterStop', 'stop.tagID','stop.tag.visualID', 'stop.reason',
+ 'stop.userID', 'stop.user.id', 'stop.user.name', 'stop.user.firstName', 'stop.user.email',
+ 'billingData.stop.invoiceNumber',
+ 'tagID', 'tag.visualID', 'tag.description',
+ 'site.name', 'siteArea.name', 'company.name',
+ 'userID', 'user.id', 'user.name', 'user.firstName', 'user.email',
+ 'carID', 'car.licensePlate',
+ 'carCatalogID', 'carCatalog.vehicleMake', 'carCatalog.vehicleModel', 'carCatalog.vehicleModelVersion',
+
+ ]
+ },
+ {
+ resource: Entity.TRANSACTION, action: Action.READ,
+ condition: {
+ Fn: 'custom:dynamicAuthorizations',
+ args: {
+ asserts: [],
+ filters: []
+ }
+ }
+ },
+ { resource: Entity.TRANSACTION, action: [Action.EXPORT, Action.IN_ERROR] },
{
resource: Entity.TRANSACTION,
action: [
- Action.READ, Action.UPDATE, Action.DELETE, Action.REFUND_TRANSACTION
+ Action.UPDATE, Action.DELETE, Action.REFUND_TRANSACTION
]
},
{ resource: Entity.REPORT, action: [Action.READ] },
@@ -1078,23 +1113,46 @@ export const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
'user.name', 'user.firstName', 'user.email', 'createdOn', 'lastChangedOn'
]
},
- { resource: Entity.TRANSACTION, action: [Action.LIST, Action.EXPORT] },
{
- resource: Entity.TRANSACTION, action: [Action.READ, Action.UPDATE],
+ resource: Entity.TRANSACTION, action: Action.LIST,
condition: {
- Fn: 'OR',
- args: [
- {
- Fn: 'EQUALS',
- args: { 'user': '$.owner' }
+ Fn: 'custom:dynamicAuthorizations',
+ args: {
+ asserts: [],
+ filters: [['AssignedSites', 'IncludeAllExternalSites']]
+ }
+ },
+ attributes: [
+ 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', 'companyID',
+ 'currentTotalDurationSecs', 'currentTotalInactivitySecs', 'currentInstantWatts', 'currentTotalConsumptionWh', 'currentStateOfCharge',
+ 'currentCumulatedPrice', 'currentInactivityStatus', 'roundedPrice', 'price', 'priceUnit', 'ocpi', 'ocpiWithCdr',
+ 'stop.roundedPrice', 'stop.price', 'stop.priceUnit', 'stop.inactivityStatus', 'stop.stateOfCharge', 'stop.timestamp', 'stop.totalConsumptionWh',
+ 'stop.totalDurationSecs', 'stop.totalInactivitySecs', 'stop.extraInactivitySecs', 'stop.meterStop', 'stop.tagID','stop.tag.visualID', 'stop.reason',
+ 'billingData.stop.invoiceNumber',
+ 'tagID', 'tag.visualID', 'tag.description',
+ 'site.name', 'siteArea.name', 'company.name',
+ 'carID',
+ 'carCatalogID', 'carCatalog.vehicleMake', 'carCatalog.vehicleModel', 'carCatalog.vehicleModelVersion',
+ ]
},
{
- Fn: 'LIST_CONTAINS',
+ resource: Entity.TRANSACTION, action: Action.READ,
+ condition: {
+ Fn: 'custom:dynamicAuthorizations',
args: {
- 'tagIDs': '$.tagID'
+ asserts: [],
+ filters: [['AssignedSites', 'IncludeAllExternalSites']]
}
}
- ]
+ },
+ {
+ resource: Entity.TRANSACTION, action: [Action.EXPORT, Action.UPDATE],
+ condition: {
+ Fn: 'custom:dynamicAuthorizations',
+ args: {
+ asserts: [],
+ filters: ['OwnUser']
+ }
}
},
{ resource: Entity.CONNECTION, action: Action.LIST },
@@ -1288,8 +1346,48 @@ export const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
]
},
{ resource: Entity.CHARGING_STATION, action: [Action.GET_CONNECTOR_QR_CODE] },
- { resource: Entity.TRANSACTION, action: Action.LIST },
- { resource: Entity.TRANSACTION, action: Action.READ },
+ {
+ resource: Entity.TRANSACTION, action: Action.LIST,
+ condition: {
+ Fn: 'custom:dynamicAuthorizations',
+ args: {
+ asserts: [],
+ filters: [['AssignedSites', 'IncludeAllExternalSites']]
+ }
+ },
+ attributes: [
+ 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', 'companyID',
+ 'currentTotalDurationSecs', 'currentTotalInactivitySecs', 'currentInstantWatts', 'currentTotalConsumptionWh', 'currentStateOfCharge',
+ 'currentCumulatedPrice', 'currentInactivityStatus', 'roundedPrice', 'price', 'priceUnit', 'ocpi', 'ocpiWithCdr',
+ 'stop.roundedPrice', 'stop.price', 'stop.priceUnit', 'stop.inactivityStatus', 'stop.stateOfCharge', 'stop.timestamp', 'stop.totalConsumptionWh',
+ 'stop.totalDurationSecs', 'stop.totalInactivitySecs', 'stop.extraInactivitySecs', 'stop.meterStop', 'stop.tagID','stop.tag.visualID', 'stop.reason',
+ 'billingData.stop.invoiceNumber',
+ 'tagID', 'tag.visualID', 'tag.description',
+ 'site.name', 'siteArea.name', 'company.name',
+ 'carID',
+ 'carCatalogID', 'carCatalog.vehicleMake', 'carCatalog.vehicleModel', 'carCatalog.vehicleModelVersion',
+ ]
+ },
+ {
+ resource: Entity.TRANSACTION, action: Action.READ,
+ condition: {
+ Fn: 'custom:dynamicAuthorizations',
+ args: {
+ asserts: [],
+ filters: [['AssignedSites', 'IncludeAllExternalSites']]
+ }
+ }
+ },
+ {
+ resource: Entity.TRANSACTION, action: [Action.EXPORT, Action.UPDATE],
+ condition: {
+ Fn: 'custom:dynamicAuthorizations',
+ args: {
+ asserts: [],
+ filters: ['AssignedSites']
+ }
+ }
+ },
]
},
siteAdmin: {
@@ -1703,11 +1801,46 @@ export const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
},
},
{
- resource: Entity.TRANSACTION, action: [Action.READ, Action.UPDATE],
+ resource: Entity.TRANSACTION, action: Action.LIST,
+ condition: {
+ Fn: 'custom:dynamicAuthorizations',
+ args: {
+ asserts: [],
+ filters: [['AssignedSites', 'IncludeAllExternalSites']]
+ }
+ },
+ attributes: [
+ 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', 'companyID',
+ 'currentTotalDurationSecs', 'currentTotalInactivitySecs', 'currentInstantWatts', 'currentTotalConsumptionWh', 'currentStateOfCharge',
+ 'currentCumulatedPrice', 'currentInactivityStatus', 'roundedPrice', 'price', 'priceUnit', 'ocpi', 'ocpiWithCdr',
+ 'stop.roundedPrice', 'stop.price', 'stop.priceUnit', 'stop.inactivityStatus', 'stop.stateOfCharge', 'stop.timestamp', 'stop.totalConsumptionWh',
+ 'stop.totalDurationSecs', 'stop.totalInactivitySecs', 'stop.extraInactivitySecs', 'stop.meterStop', 'stop.tagID','stop.tag.visualID', 'stop.reason',
+ 'billingData.stop.invoiceNumber',
+ 'tagID', 'tag.visualID', 'tag.description',
+ 'site.name', 'siteArea.name', 'company.name',
+ 'carID',
+ 'carCatalogID', 'carCatalog.vehicleMake', 'carCatalog.vehicleModel', 'carCatalog.vehicleModelVersion',
+ ]
+ },
+ {
+ resource: Entity.TRANSACTION, action: Action.READ,
condition: {
- Fn: 'LIST_CONTAINS',
- args: { 'sitesAdmin': '$.site' }
+ Fn: 'custom:dynamicAuthorizations',
+ args: {
+ asserts: [],
+ filters: [['AssignedSites', 'IncludeAllExternalSites']]
+ }
+ }
},
+ {
+ resource: Entity.TRANSACTION, action: [Action.UPDATE, Action.EXPORT],
+ condition: {
+ Fn: 'custom:dynamicAuthorizations',
+ args: {
+ asserts: [],
+ filters: ['SitesAdmin']
+ }
+ }
},
{ resource: Entity.REPORT, action: [Action.READ] },
{
@@ -1941,10 +2074,45 @@ export const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
],
},
{
- resource: Entity.TRANSACTION, action: [Action.READ, Action.REFUND_TRANSACTION],
+ resource: Entity.TRANSACTION, action: Action.LIST,
+ condition: {
+ Fn: 'custom:dynamicAuthorizations',
+ args: {
+ asserts: [],
+ filters: [['AssignedSites', 'IncludeAllExternalSites']]
+ }
+ },
+ attributes: [
+ 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', 'companyID',
+ 'currentTotalDurationSecs', 'currentTotalInactivitySecs', 'currentInstantWatts', 'currentTotalConsumptionWh', 'currentStateOfCharge',
+ 'currentCumulatedPrice', 'currentInactivityStatus', 'roundedPrice', 'price', 'priceUnit', 'ocpi', 'ocpiWithCdr',
+ 'stop.roundedPrice', 'stop.price', 'stop.priceUnit', 'stop.inactivityStatus', 'stop.stateOfCharge', 'stop.timestamp', 'stop.totalConsumptionWh',
+ 'stop.totalDurationSecs', 'stop.totalInactivitySecs', 'stop.extraInactivitySecs', 'stop.meterStop', 'stop.tagID','stop.tag.visualID', 'stop.reason',
+ 'billingData.stop.invoiceNumber',
+ 'tagID', 'tag.visualID', 'tag.description',
+ 'site.name', 'siteArea.name', 'company.name',
+ 'carID',
+ 'carCatalogID', 'carCatalog.vehicleMake', 'carCatalog.vehicleModel', 'carCatalog.vehicleModelVersion',
+ ]
+ },
+ {
+ resource: Entity.TRANSACTION, action: Action.READ,
condition: {
- Fn: 'LIST_CONTAINS',
- args: { 'sitesOwner': '$.site' }
+ Fn: 'custom:dynamicAuthorizations',
+ args: {
+ asserts: [],
+ filters: [['AssignedSites', 'IncludeAllExternalSites']]
+ }
+ }
+ },
+ {
+ resource: Entity.TRANSACTION, action: [Action.UPDATE, Action.EXPORT, Action.REFUND_TRANSACTION],
+ condition: {
+ Fn: 'custom:dynamicAuthorizations',
+ args: {
+ asserts: [],
+ filters: ['SitesOwner']
+ }
}
},
{ resource: Entity.REPORT, action: [Action.READ] },

MODIFY
src/server/rest/v1/service/AuthorizationService.ts
src/server/rest/v1/service/AuthorizationService.ts
@@ -1069,7 +1069,7 @@ export default class AuthorizationService {
}
// Transaction NEW
- public static async checkAndGetTransactionsAuthorizations(tenant: Tenant, userToken: UserToken,
+ public static async checkAndGetTransactionsAuthorizations(tenant: Tenant, userToken: UserToken, authAction: Action,
filteredRequest: Partial<HttpTransactionsGetRequest>, failsWithException = true): Promise<AuthorizationFilter> {
const authorizations: AuthorizationFilter = {
filters: {},
@@ -1079,7 +1079,7 @@ export default class AuthorizationService {
};
// Check static & dynamic authorization
await AuthorizationService.canPerformAuthorizationAction(
- tenant, userToken, Entity.TRANSACTION, Action.LIST, authorizations, filteredRequest, null, failsWithException);
+ tenant, userToken, Entity.TRANSACTION, authAction, authorizations, filteredRequest, null, failsWithException);
return authorizations;
}

MODIFY
src/server/rest/v1/service/TransactionService.ts
src/server/rest/v1/service/TransactionService.ts
@@ -8,6 +8,7 @@ import Transaction, { AdvenirConsumptionData, AdvenirEvseData, AdvenirPayload, A
import { ActionsResponse } from '../../../../types/GlobalType';
import AppAuthError from '../../../../exception/AppAuthError';
import AppError from '../../../../exception/AppError';
+import AuthorizationService from './AuthorizationService';
import Authorizations from '../../../../authorization/Authorizations';
import BillingFactory from '../../../../integration/billing/BillingFactory';
import { BillingStatus } from '../../../../types/Billing';
@@ -51,15 +52,16 @@ export default class TransactionService {
// Filter
const filteredRequest = TransactionValidatorRest.getInstance().validateTransactionsGetReq(req.query);
// Get Transactions
- const transactions = await TransactionService.getTransactions(req, action, {}, filteredRequest, [
- 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', 'companyID',
- 'currentTotalDurationSecs', 'currentTotalInactivitySecs', 'currentInstantWatts', 'currentTotalConsumptionWh', 'currentStateOfCharge',
- 'currentCumulatedPrice', 'currentInactivityStatus', 'roundedPrice', 'price', 'priceUnit',
- 'stop.roundedPrice', 'stop.price', 'stop.priceUnit', 'stop.inactivityStatus', 'stop.stateOfCharge', 'stop.timestamp', 'stop.totalConsumptionWh',
- 'stop.totalDurationSecs', 'stop.totalInactivitySecs', 'stop.extraInactivitySecs', 'stop.meterStop',
- 'billingData.stop.invoiceNumber', 'stop.reason', 'ocpi', 'ocpiWithCdr', 'tagID', 'stop.tagID', 'tag.visualID', 'tag.description', 'stop.tag.visualID',
- 'site.name', 'siteArea.name', 'company.name'
- ]);
+ // const transactions = await TransactionService.getTransactions(req, action, {}, filteredRequest, [
+ // 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', 'companyID',
+ // 'currentTotalDurationSecs', 'currentTotalInactivitySecs', 'currentInstantWatts', 'currentTotalConsumptionWh', 'currentStateOfCharge',
+ // 'currentCumulatedPrice', 'currentInactivityStatus', 'roundedPrice', 'price', 'priceUnit',
+ // 'stop.roundedPrice', 'stop.price', 'stop.priceUnit', 'stop.inactivityStatus', 'stop.stateOfCharge', 'stop.timestamp', 'stop.totalConsumptionWh',
+ // 'stop.totalDurationSecs', 'stop.totalInactivitySecs', 'stop.extraInactivitySecs', 'stop.meterStop',
+ // 'billingData.stop.invoiceNumber', 'stop.reason', 'ocpi', 'ocpiWithCdr', 'tagID', 'stop.tagID', 'tag.visualID', 'tag.description', 'stop.tag.visualID',
+ // 'site.name', 'siteArea.name', 'company.name'
+ // ]);
+ const transactions = await TransactionService.getTransactions_new(req, filteredRequest);
res.json(transactions);
next();
}
@@ -1076,6 +1078,72 @@ export default class TransactionService {
return result;
}
+ private static async getTransactions_new(req: Request, filteredRequest: HttpTransactionsGetRequest,
+ authAction: Action = Action.LIST, additionalFilters: Record<string, any> = {}): Promise<DataResult<Transaction>> {
+
+ // Get authorization filters
+ const authorizations = await AuthorizationService.checkAndGetTransactionsAuthorizations(
+ req.tenant, req.user, authAction, filteredRequest, false);
+ if (!authorizations.authorized) {
+ return Constants.DB_EMPTY_DATA_RESULT;
+ }
+
+ // Get Tag IDs from Visual IDs
+ if (filteredRequest.VisualTagID) {
+ const tagIDs = await TagStorage.getTags(req.tenant, { visualIDs: filteredRequest.VisualTagID.split('|') }, Constants.DB_PARAMS_MAX_LIMIT, ['id']);
+ if (!Utils.isEmptyArray(tagIDs.result)) {
+ filteredRequest.TagID = tagIDs.result.map((tag) => tag.id).join('|');
+ }
+ }
+ // Get the transactions
+ const transactions = await TransactionStorage.getTransactions(req.tenant,
+ {
+ search: filteredRequest.Search ? filteredRequest.Search : null,
+ status: filteredRequest.Status ,
+ chargingStationIDs: filteredRequest.ChargingStationID ? filteredRequest.ChargingStationID.split('|') : null,
+ issuer: Utils.objectHasProperty(filteredRequest, 'Issuer') ? filteredRequest.Issuer : null,
+ userIDs: filteredRequest.UserID ? filteredRequest.UserID.split('|') : null,
+ tagIDs: filteredRequest.TagID ? filteredRequest.TagID.split('|') : null,
+ // ownerID: Authorizations.isBasic(req.user) ? req.user.id : null,
+ withTag: filteredRequest.WithTag,
+ withUser: filteredRequest.WithUser,
+ withChargingStation: filteredRequest.WithChargingStation,
+ withCar: filteredRequest.WithCar,
+ withSite: filteredRequest.WithSite,
+ withCompany: filteredRequest.WithCompany,
+ withSiteArea: filteredRequest.WithSiteArea,
+ siteIDs: (filteredRequest.SiteID ? filteredRequest.SiteID.split('|') : null),
+ siteAreaIDs: filteredRequest.SiteAreaID ? filteredRequest.SiteAreaID.split('|') : null,
+ // siteAdminIDs: await Authorizations.getAuthorizedSiteAdminIDs(req.tenant, req.user),
+ startDateTime: filteredRequest.StartDateTime ? filteredRequest.StartDateTime : null,
+ endDateTime: filteredRequest.EndDateTime ? filteredRequest.EndDateTime : null,
+ refundStatus: filteredRequest.RefundStatus ? filteredRequest.RefundStatus.split('|') as RefundStatus[] : null,
+ minimalPrice: filteredRequest.MinimalPrice ? filteredRequest.MinimalPrice : null,
+ statistics: filteredRequest.Statistics ? filteredRequest.Statistics : null,
+ reportIDs: filteredRequest.ReportIDs ? filteredRequest.ReportIDs.split('|') : null,
+ connectorIDs: filteredRequest.ConnectorID ? filteredRequest.ConnectorID.split('|').map((connectorID) => Utils.convertToInt(connectorID)) : null,
+ inactivityStatus: filteredRequest.InactivityStatus ? filteredRequest.InactivityStatus.split('|') : null,
+ ...authorizations.filters
+ },
+ {
+ limit: filteredRequest.Limit,
+ skip: filteredRequest.Skip,
+ sort: UtilsService.httpSortFieldsToMongoDB(filteredRequest.SortFields),
+ onlyRecordCount: filteredRequest.OnlyRecordCount,
+ },
+ authorizations.projectFields
+ );
+ // Assign projected fields
+ if (authorizations.projectFields) {
+ transactions.projectFields = authorizations.projectFields;
+ }
+ // Add Auth flags
+ await AuthorizationService.addTransactionsAuthorizations(
+ req.tenant, req.user, transactions, authorizations);
+
+ return transactions;
+ }
+
private static async getTransactions(req: Request, action: ServerAction, params: { completedTransactions?: boolean, withTag?: boolean } = {},
filteredRequest: HttpTransactionsGetRequest, projectFields): Promise<DataResult<Transaction>> {
// Check Transactions
@@ -1127,7 +1195,7 @@ export default class TransactionService {
// Get the transactions
const transactions = await TransactionStorage.getTransactions(req.tenant,
{
- status: filteredRequest.Status as TransactionStatus,
+ status: filteredRequest.Status,
chargingStationIDs: filteredRequest.ChargingStationID ? filteredRequest.ChargingStationID.split('|') : null,
issuer: Utils.objectHasProperty(filteredRequest, 'Issuer') ? filteredRequest.Issuer : null,
userIDs: filteredRequest.UserID ? filteredRequest.UserID.split('|') : null,

Add auth for LIST/READ