Enhanced Registration token
MODIFY
src/authorization/Authorizations.ts
src/authorization/Authorizations.ts
@@ -387,12 +387,18 @@ export default class Authorizations {
});
}
- public static canDeleteRegistrationToken(loggedUser: UserToken): boolean {
- return Authorizations.canPerformAction(loggedUser, Entity.TOKEN, Action.DELETE);
+ public static canDeleteRegistrationToken(loggedUser: UserToken, siteID: string): boolean {
+ return Authorizations.canPerformAction(loggedUser, Entity.TOKEN, Action.DELETE, {
+ site: siteID,
+ sites: loggedUser.sitesAdmin
+ });
}
- public static canUpdateRegistrationToken(loggedUser: UserToken): boolean {
- return Authorizations.canPerformAction(loggedUser, Entity.TOKEN, Action.UPDATE);
+ public static canUpdateRegistrationToken(loggedUser: UserToken, siteID: string): boolean {
+ return Authorizations.canPerformAction(loggedUser, Entity.TOKEN, Action.UPDATE, {
+ site: siteID,
+ sites: loggedUser.sitesAdmin
+ });
}
public static canListRegistrationTokens(loggedUser: UserToken): boolean {

MODIFY
src/authorization/AuthorizationsDefinition.ts
src/authorization/AuthorizationsDefinition.ts
@@ -284,7 +284,7 @@ const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
{ resource: Entity.TOKENS, action: Action.LIST, attributes: ['*'] },
{
resource: Entity.TOKEN,
- action: [Action.CREATE, Action.READ],
+ action: [Action.CREATE, Action.READ, Action.UPDATE, Action.DELETE],
attributes: ['*'],
args: { 'sites': '$.site' }
},

MODIFY
src/server/rest/service/RegistrationTokenService.ts
src/server/rest/service/RegistrationTokenService.ts
@@ -24,6 +24,7 @@ export default class RegistrationTokenService {
static async handleCreateRegistrationToken(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
// Filter
const filteredRequest = RegistrationTokenSecurity.filterRegistrationTokenCreateRequest(req.body);
+ // Check Auth
if (Utils.isComponentActiveFromToken(req.user, TenantComponents.ORGANIZATION) && filteredRequest.siteAreaID) {
// Get the Site Area
const siteArea = await SiteAreaStorage.getSiteArea(req.user.tenantID, filteredRequest.siteAreaID);
@@ -47,6 +48,7 @@ export default class RegistrationTokenService {
module: MODULE_NAME, method: 'handleCreateRegistrationToken'
});
}
+ // Check
if (!filteredRequest.description) {
throw new AppError({
source: Constants.CENTRAL_SERVER,
@@ -56,6 +58,7 @@ export default class RegistrationTokenService {
user: req.user
});
}
+ // Create
const registrationToken: RegistrationToken = {
siteAreaID: filteredRequest.siteAreaID,
description: filteredRequest.description,
@@ -63,8 +66,9 @@ export default class RegistrationTokenService {
createdBy: { id: req.user.id },
createdOn: new Date()
};
- const registrationTokenID = await RegistrationTokenStorage.saveRegistrationToken(req.user.tenantID, registrationToken);
- registrationToken.id = registrationTokenID;
+ // Save
+ registrationToken.id = await RegistrationTokenStorage.saveRegistrationToken(req.user.tenantID, registrationToken);
+ // Build OCPP URLs
registrationToken.ocpp15SOAPUrl = Utils.buildOCPPServerURL(req.user.tenantID, OCPPVersion.VERSION_15, OCPPProtocol.SOAP, registrationToken.id);
registrationToken.ocpp16SOAPUrl = Utils.buildOCPPServerURL(req.user.tenantID, OCPPVersion.VERSION_16, OCPPProtocol.SOAP, registrationToken.id);
registrationToken.ocpp16JSONUrl = Utils.buildOCPPServerURL(req.user.tenantID, OCPPVersion.VERSION_16, OCPPProtocol.JSON, registrationToken.id);
@@ -73,20 +77,66 @@ export default class RegistrationTokenService {
next();
}
- static async handleDeleteRegistrationToken(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
- const tokenID = RegistrationTokenSecurity.filterRegistrationTokenByIDRequest(req.query);
- // Check Mandatory fields
- if (!tokenID) {
+ static async handleUpdateRegistrationToken(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ // Filter
+ const filteredRequest = RegistrationTokenSecurity.filterRegistrationTokenUpdateRequest(req.body);
+ UtilsService.assertIdIsProvided(action, filteredRequest.id, MODULE_NAME, 'handleUpdateRegistrationToken', req.user);
+ // Get Token
+ const registrationToken = await RegistrationTokenStorage.getRegistrationToken(req.user.tenantID, filteredRequest.id);
+ UtilsService.assertObjectExists(action, registrationToken, `Token ID '${filteredRequest.id}' does not exist`,
+ MODULE_NAME, 'handleUpdateRegistrationToken', req.user);
+ if (Utils.isComponentActiveFromToken(req.user, TenantComponents.ORGANIZATION)) {
+ // Check Site Area
+ const siteArea = await SiteAreaStorage.getSiteArea(req.user.tenantID, filteredRequest.siteAreaID);
+ UtilsService.assertObjectExists(action, siteArea, `Site Area ID '${filteredRequest.siteAreaID}' does not exist`,
+ MODULE_NAME, 'handleUpdateRegistrationToken', req.user);
+ }
+ // Check Auth
+ if (!Authorizations.canUpdateRegistrationToken(req.user, registrationToken.siteArea?.siteID)) {
+ // Not Authorized!
+ throw new AppAuthError({
+ errorCode: HTTPAuthError.ERROR,
+ user: req.user,
+ action: Action.UPDATE, entity: Entity.TOKEN,
+ module: MODULE_NAME, method: 'handleUpdateRegistrationToken'
+ });
+ }
+ // Check
+ if (!filteredRequest.description) {
throw new AppError({
source: Constants.CENTRAL_SERVER,
errorCode: HTTPError.GENERAL_ERROR,
- message: 'Registration Token\'s ID must be provided',
- module: MODULE_NAME, method: 'handleDeleteRegistrationToken',
+ message: 'The description must be provided',
+ module: MODULE_NAME, method: 'handleUpdateRegistrationToken',
user: req.user
});
}
+ // Update
+ registrationToken.siteAreaID = filteredRequest.siteAreaID;
+ registrationToken.description = filteredRequest.description;
+ registrationToken.expirationDate = filteredRequest.expirationDate ? filteredRequest.expirationDate : moment().add(1, 'month').toDate();
+ registrationToken.lastChangedBy = { id: req.user.id };
+ registrationToken.lastChangedOn = new Date();
+ // Save
+ registrationToken.id = await RegistrationTokenStorage.saveRegistrationToken(req.user.tenantID, registrationToken);
+ // Build OCPP URLs
+ registrationToken.ocpp15SOAPUrl = Utils.buildOCPPServerURL(req.user.tenantID, OCPPVersion.VERSION_15, OCPPProtocol.SOAP, registrationToken.id);
+ registrationToken.ocpp16SOAPUrl = Utils.buildOCPPServerURL(req.user.tenantID, OCPPVersion.VERSION_16, OCPPProtocol.SOAP, registrationToken.id);
+ registrationToken.ocpp16JSONUrl = Utils.buildOCPPServerURL(req.user.tenantID, OCPPVersion.VERSION_16, OCPPProtocol.JSON, registrationToken.id);
+ // Ok
+ res.json(RegistrationTokenSecurity.filterRegistrationTokenResponse(registrationToken, req.user));
+ next();
+ }
+
+ static async handleDeleteRegistrationToken(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ const tokenID = RegistrationTokenSecurity.filterRegistrationTokenByIDRequest(req.query);
+ UtilsService.assertIdIsProvided(action, tokenID, MODULE_NAME, 'handleDeleteRegistrationToken', req.user);
+ // Get Token
+ const registrationToken = await RegistrationTokenStorage.getRegistrationToken(req.user.tenantID, tokenID);
+ UtilsService.assertObjectExists(action, registrationToken, `Registration Token '${tokenID}' does not exist`,
+ MODULE_NAME, 'handleDeleteRegistrationToken', req.user);
// Check auth
- if (!Authorizations.canDeleteRegistrationToken(req.user)) {
+ if (!Authorizations.canDeleteRegistrationToken(req.user, registrationToken.siteArea?.siteID)) {
throw new AppAuthError({
errorCode: HTTPAuthError.ERROR,
user: req.user,
@@ -95,10 +145,7 @@ export default class RegistrationTokenService {
value: tokenID
});
}
- // Check user
- const registrationToken = await RegistrationTokenStorage.getRegistrationToken(req.user.tenantID, tokenID);
- UtilsService.assertObjectExists(action, registrationToken, `Registration Token '${tokenID}' does not exist`,
- MODULE_NAME, 'handleDeleteRegistrationToken', req.user);
+ // Delete
await RegistrationTokenStorage.deleteRegistrationToken(req.user.tenantID, tokenID);
// Log
Logging.logSecurityInfo({
@@ -115,18 +162,13 @@ export default class RegistrationTokenService {
static async handleRevokeRegistrationToken(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
const tokenID = RegistrationTokenSecurity.filterRegistrationTokenByIDRequest(req.query);
- // Check Mandatory fields
- if (!tokenID) {
- throw new AppError({
- source: Constants.CENTRAL_SERVER,
- errorCode: HTTPError.GENERAL_ERROR,
- message: 'Registration Token\'s ID must be provided',
- module: MODULE_NAME, method: 'handleRevokeRegistrationToken',
- user: req.user
- });
- }
+ UtilsService.assertIdIsProvided(action, tokenID, MODULE_NAME, 'handleDeleteRegistrationToken', req.user);
+ // Get Token
+ const registrationToken = await RegistrationTokenStorage.getRegistrationToken(req.user.tenantID, tokenID);
+ UtilsService.assertObjectExists(action, registrationToken, `Registration Token '${tokenID}' does not exist`,
+ MODULE_NAME, 'handleRevokeRegistrationToken', req.user);
// Check auth
- if (!Authorizations.canUpdateRegistrationToken(req.user)) {
+ if (!Authorizations.canUpdateRegistrationToken(req.user, registrationToken.siteArea?.siteID)) {
throw new AppAuthError({
errorCode: HTTPAuthError.ERROR,
user: req.user,
@@ -135,10 +177,7 @@ export default class RegistrationTokenService {
value: tokenID
});
}
- // Check user
- const registrationToken = await RegistrationTokenStorage.getRegistrationToken(req.user.tenantID, tokenID);
- UtilsService.assertObjectExists(action, registrationToken, `Registration Token '${tokenID}' does not exist`,
- MODULE_NAME, 'handleRevokeRegistrationToken', req.user);
+ // Update
registrationToken.revocationDate = new Date();
registrationToken.lastChangedBy = { 'id': req.user.id };
registrationToken.lastChangedOn = new Date();
@@ -181,7 +220,7 @@ export default class RegistrationTokenService {
onlyRecordCount: filteredRequest.OnlyRecordCount
}
);
- // Build OCPP URL
+ // Build OCPP URLs
registrationTokens.result.forEach((registrationToken) => {
registrationToken.ocpp15SOAPUrl = Utils.buildOCPPServerURL(req.user.tenantID, OCPPVersion.VERSION_15, OCPPProtocol.SOAP, registrationToken.id);
registrationToken.ocpp16SOAPUrl = Utils.buildOCPPServerURL(req.user.tenantID, OCPPVersion.VERSION_16, OCPPProtocol.SOAP, registrationToken.id);
@@ -190,6 +229,7 @@ export default class RegistrationTokenService {
});
// Filter
RegistrationTokenSecurity.filterRegistrationTokensResponse(registrationTokens, req.user);
+ // Ok
res.json(registrationTokens);
next();
}

MODIFY
src/server/rest/service/SiteAreaService.ts
src/server/rest/service/SiteAreaService.ts
@@ -473,7 +473,7 @@ export default class SiteAreaService {
Action.UPDATE, Entity.SITE_AREA, MODULE_NAME, 'handleUpdateSiteArea');
// Filter
const filteredRequest = SiteAreaSecurity.filterSiteAreaUpdateRequest(req.body);
- // Get
+ // Get Site Area
const siteArea = await SiteAreaStorage.getSiteArea(req.user.tenantID, filteredRequest.id, { withChargingStations: true });
UtilsService.assertObjectExists(action, siteArea, `Site Area with ID '${filteredRequest.id}' does not exist`,
MODULE_NAME, 'handleUpdateSiteArea', req.user);

MODIFY
src/server/rest/service/security/RegistrationTokenSecurity.ts
src/server/rest/service/security/RegistrationTokenSecurity.ts
-import { HttpRegistrationTokenRequest, HttpRegistrationTokensRequest } from '../../../../types/requests/HttpRegistrationToken';
-
import Authorizations from '../../../../authorization/Authorizations';
import { DataResult } from '../../../../types/DataResult';
+import { HttpRegistrationTokensRequest } from '../../../../types/requests/HttpRegistrationToken';
import RegistrationToken from '../../../../types/RegistrationToken';
import UserToken from '../../../../types/UserToken';
import UtilsSecurity from './UtilsSecurity';
import sanitize from 'mongo-sanitize';
export default class RegistrationTokenSecurity {
- static filterRegistrationTokenCreateRequest(request: any): HttpRegistrationTokenRequest {
+ public static filterRegistrationTokenCreateRequest(request: any): Partial<RegistrationToken> {
+ return {
+ description: sanitize(request.description),
+ siteAreaID: sanitize(request.siteAreaID),
+ expirationDate: sanitize(request.expirationDate)
+ };
+ }
+
+ public static filterRegistrationTokenUpdateRequest(request: any): Partial<RegistrationToken> {
+ return {
+ id: sanitize(request.id),
+ description: sanitize(request.description),
+ siteAreaID: sanitize(request.siteAreaID),
+ expirationDate: sanitize(request.expirationDate)
+ };
+ }
+
+ public static filterRegistrationTokenRequest(request: any): Partial<RegistrationToken> {
return {
description: sanitize(request.description),
siteAreaID: sanitize(request.siteAreaID),

MODIFY
src/storage/mongodb/RegistrationTokenStorage.ts
src/storage/mongodb/RegistrationTokenStorage.ts
@@ -19,16 +19,16 @@ export default class RegistrationTokenStorage {
await Utils.checkTenant(tenantID);
// Set
const registrationTokenMDB = {
- _id: !registrationToken.id ? new ObjectID() : Utils.convertToObjectID(registrationToken.id),
+ _id: registrationToken.id ? Utils.convertToObjectID(registrationToken.id) : new ObjectID(),
description: registrationToken.description,
- siteAreaID: registrationToken.siteAreaID ? Utils.convertToObjectID(registrationToken.siteAreaID) : null,
- expirationDate: registrationToken.expirationDate,
- revocationDate: registrationToken.revocationDate
+ siteAreaID: Utils.convertToObjectID(registrationToken.siteAreaID),
+ expirationDate: Utils.convertToDate(registrationToken.expirationDate),
+ revocationDate: Utils.convertToDate(registrationToken.revocationDate)
};
// Add Last Changed/Created props
DatabaseUtils.addLastChangedCreatedProps(registrationTokenMDB, registrationToken);
// Modify
- await global.database.getCollection<RegistrationTokenStorage>(tenantID, 'registrationtokens').findOneAndUpdate(
+ await global.database.getCollection(tenantID, 'registrationtokens').findOneAndUpdate(
{ _id: registrationTokenMDB._id },
{ $set: registrationTokenMDB },
{ upsert: true, returnOriginal: false }
@@ -39,7 +39,7 @@ export default class RegistrationTokenStorage {
}
static async getRegistrationTokens(tenantID: string,
- params: { id?: string; siteIDs?: string[]; siteAreaID?: string } = {}, dbParams: DbParams):
+ params: { tokenIDs?: string[]; siteIDs?: string[]; siteAreaID?: string } = {}, dbParams: DbParams):
Promise<DataResult<RegistrationToken>> {
// Debug
const uniqueTimerID = Logging.traceStart(MODULE_NAME, 'getRegistrationTokens');
@@ -51,6 +51,13 @@ export default class RegistrationTokenStorage {
dbParams.limit = Utils.checkRecordLimit(dbParams.limit);
// Check Skip
dbParams.skip = Utils.checkRecordSkip(dbParams.skip);
+ // Create Aggregation
+ const aggregation = [];
+ // Add Site Area
+ DatabaseUtils.pushSiteAreaLookupInAggregation({
+ tenantID, aggregation, localField: 'siteAreaID', foreignField: '_id',
+ asField: 'siteArea', oneToOneCardinality: true
+ });
// Set the filters
const filters: FilterParams = {};
// Build filter
@@ -58,21 +65,17 @@ export default class RegistrationTokenStorage {
filters.siteAreaID = Utils.convertToObjectID(params.siteAreaID);
}
// Build filter
- if (params.id) {
- filters._id = Utils.convertToObjectID(params.id);
+ if (!Utils.isEmptyArray(params.tokenIDs)) {
+ filters._id = {
+ $in: params.tokenIDs.map((tokenID) => Utils.convertToObjectID(tokenID))
+ };
}
+ // Sites
if (!Utils.isEmptyArray(params.siteIDs)) {
- // Build filter
filters['siteArea.siteID'] = {
$in: params.siteIDs.map((siteID) => Utils.convertToObjectID(siteID))
};
}
- // Create Aggregation
- const aggregation = [];
- DatabaseUtils.pushSiteAreaLookupInAggregation({
- tenantID, aggregation, localField: 'siteAreaID', foreignField: '_id',
- asField: 'siteArea', oneToOneCardinality: true
- });
// Filters
if (filters) {
aggregation.push({
@@ -134,15 +137,11 @@ export default class RegistrationTokenStorage {
// Debug
const uniqueTimerID = Logging.traceStart(MODULE_NAME, 'getRegistrationToken');
// Reuse
- const registrationTokens = await RegistrationTokenStorage.getRegistrationTokens(tenantID, { id: id }, Constants.DB_PARAMS_SINGLE_RECORD);
- let registrationToken: RegistrationToken = null;
- // Check
- if (registrationTokens && registrationTokens.count > 0) {
- registrationToken = registrationTokens.result[0];
- }
+ const registrationTokensMDB = await RegistrationTokenStorage.getRegistrationTokens(
+ tenantID, { tokenIDs: [id] }, Constants.DB_PARAMS_SINGLE_RECORD);
// Debug
Logging.traceEnd(MODULE_NAME, 'getRegistrationToken', uniqueTimerID, { id });
- return registrationToken;
+ return registrationTokensMDB.count === 1 ? registrationTokensMDB.result[0] : null;
}
static async deleteRegistrationToken(tenantID: string, id: string): Promise<void> {

MODIFY
src/types/requests/HttpRegistrationToken.ts
src/types/requests/HttpRegistrationToken.ts
import HttpDatabaseRequest from './HttpDatabaseRequest';
import RegistrationToken from '../RegistrationToken';
-export interface HttpRegistrationTokenRequest {
- description: string;
- siteAreaID: string;
- expirationDate: Date;
-}
-
export interface HttpRegistrationTokensRequest extends HttpDatabaseRequest {
siteAreaID: string;
}

Enhanced Registration token