Redirect simplification
* Redirect simplification proposal
Always try to get the tenant in unprotected endpoints
Check that tenant is provided in handleXXX methods
* Fixed undefined error status code returned to HTTP request
MODIFY
src/server/rest/v1/service/AssetService.ts
src/server/rest/v1/service/AssetService.ts
@@ -318,6 +318,14 @@ export default class AssetService {
}
public static async handleGetAssetImage(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ // Check Tenant
+ if (!req.tenant) {
+ throw new AppError({
+ errorCode: StatusCodes.BAD_REQUEST,
+ message: 'Tenant must be provided',
+ module: MODULE_NAME, method: 'handleGetAssetImage', action: action,
+ });
+ }
// This endpoint is not protected, so no need to check user's access
const filteredRequest = AssetValidatorRest.getInstance().validateAssetGetImageReq(req.query);
// Get the image

MODIFY
src/server/rest/v1/service/AuthService.ts
src/server/rest/v1/service/AuthService.ts
@@ -57,6 +57,14 @@ export default class AuthService {
}
public static async handleLogIn(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ // Check Tenant
+ if (!req.tenant) {
+ throw new AppError({
+ errorCode: StatusCodes.BAD_REQUEST,
+ message: 'Tenant must be provided',
+ module: MODULE_NAME, method: 'handleLogIn', action: action,
+ });
+ }
// Filter
const filteredRequest = AuthValidatorRest.getInstance().validateAuthSignInReq(req.body);
req.user = { tenantID: req.tenant.id };
@@ -108,6 +116,14 @@ export default class AuthService {
}
public static async handleRegisterUser(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ // Check Tenant
+ if (!req.tenant) {
+ throw new AppError({
+ errorCode: StatusCodes.BAD_REQUEST,
+ message: 'Tenant must be provided',
+ module: MODULE_NAME, method: 'handleRegisterUser', action: action,
+ });
+ }
// Filter
const filteredRequest = AuthValidatorRest.getInstance().validateAuthSignOnReq(req.body);
// Override
@@ -293,6 +309,15 @@ export default class AuthService {
}
public static async handleUserPasswordReset(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ // Check Tenant
+ if (!req.tenant) {
+ throw new AppError({
+ errorCode: StatusCodes.BAD_REQUEST,
+ message: 'Tenant must be provided',
+ module: MODULE_NAME, method: 'handleUserPasswordReset', action: action,
+ });
+ }
+ // Filter
const filteredRequest = AuthValidatorRest.getInstance().validateAuthPasswordResetReq(req.body);
// Check hash
if (filteredRequest.hash) {
@@ -305,6 +330,14 @@ export default class AuthService {
}
public static async handleCheckEndUserLicenseAgreement(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ // Check Tenant
+ if (!req.tenant) {
+ throw new AppError({
+ errorCode: StatusCodes.BAD_REQUEST,
+ message: 'Tenant must be provided',
+ module: MODULE_NAME, method: 'handleCheckEndUserLicenseAgreement', action: action,
+ });
+ }
// Filter
const filteredRequest = AuthValidatorRest.getInstance().validateAuthEulaCheckReq(req.query);
// Get User
@@ -350,6 +383,14 @@ export default class AuthService {
}
public static async handleVerifyEmail(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ // Check Tenant
+ if (!req.tenant) {
+ throw new AppError({
+ errorCode: StatusCodes.BAD_REQUEST,
+ message: 'Tenant must be provided',
+ module: MODULE_NAME, method: 'handleVerifyEmail', action: action,
+ });
+ }
// Filter
const filteredRequest = AuthValidatorRest.getInstance().validateAuthEmailVerifyReq(req.query);
// Check that this is not the super tenant
@@ -448,6 +489,15 @@ export default class AuthService {
}
public static async handleResendVerificationEmail(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ // Check Tenant
+ if (!req.tenant) {
+ throw new AppError({
+ errorCode: StatusCodes.BAD_REQUEST,
+ message: 'Tenant must be provided',
+ module: MODULE_NAME, method: 'handleResendVerificationEmail', action: action,
+ });
+ }
+ // Filter
const filteredRequest = AuthValidatorRest.getInstance().validateAuthVerificationEmailResendReq(req.body);
// Check that this is not the super tenant
if (req.tenant.id === Constants.DEFAULT_TENANT_ID) {

MODIFY
src/server/rest/v1/service/BillingService.ts
src/server/rest/v1/service/BillingService.ts
@@ -508,6 +508,14 @@ export default class BillingService {
}
public static async handleActivateSubAccount(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ // Check Tenant
+ if (!req.tenant) {
+ throw new AppError({
+ errorCode: StatusCodes.BAD_REQUEST,
+ message: 'Tenant must be provided',
+ module: MODULE_NAME, method: 'handleActivateSubAccount', action: action,
+ });
+ }
const filteredRequest = BillingValidatorRest.getInstance().validateBillingActivateSubAccountReq({ ...req.params, ...req.body });
const subAccount = await BillingStorage.getSubAccountByID(req.tenant, filteredRequest.ID);
UtilsService.assertObjectExists(action, subAccount, `Sub account ID '${filteredRequest.ID}' does not exist`, MODULE_NAME, 'handleActivateSubAccount', req.user);

MODIFY
src/server/rest/v1/service/CommonService.ts
src/server/rest/v1/service/CommonService.ts
@@ -4,7 +4,6 @@ import { ReasonPhrases, StatusCodes } from 'http-status-codes';
import AppError from '../../../../exception/AppError';
import AuthService from './AuthService';
import CommonValidatorRest from '../validator/CommonValidatorRest';
-import { HTTPError } from '../../../../types/HTTPError';
import Tenant from '../../../../types/Tenant';
import TenantStorage from '../../../../storage/mongodb/TenantStorage';
import Utils from '../../../../utils/Utils';
@@ -14,21 +13,14 @@ const MODULE_NAME = 'CommonService';
export default class CommonService {
public static async checkTenantValidity(req: Request, res: Response, next: NextFunction): Promise<void> {
- // Methods to ignore
- if (req.url !== '/signout' &&
- req.url !== '/ping' &&
- !req.url.startsWith('/car-catalogs') &&
- !req.url.startsWith('/charging-stations/firmware/download') &&
- !req.url.startsWith('/billing/sub-accounts') &&
- !req.url.startsWith('/eula?') &&
- req.url !== '/eula') {
try {
+ // Get Tenant ID/Sub Domain from HTTP Request
const httpRequest = {
...Utils.cloneObject(req.body),
...Utils.cloneObject(req.query)
};
- // Get Tenant ID/Sub Domain from HTTP Request
const filteredRequest = CommonValidatorRest.getInstance().validateAuthVerifyTenantRedirectReq(httpRequest);
+ // Get the Tenant information
let tenantID: string;
let tenantSubdomain: string;
if (filteredRequest.tenant) {
@@ -46,20 +38,15 @@ export default class CommonService {
if (filteredRequest.ID && req.url.startsWith('/tenants/logo')) {
tenantID = filteredRequest.ID;
}
+ // No Tenant info found
if (!tenantID && !tenantSubdomain) {
// Handle the default tenant
- if (Object.prototype.hasOwnProperty.call(httpRequest, 'tenant')) {
+ if (Utils.objectHasProperty(httpRequest, 'tenant')) {
req.tenant = await AuthService.getTenant('');
+ }
next();
return;
}
- throw new AppError({
- errorCode: HTTPError.GENERAL_ERROR,
- message: 'The Tenant ID or Subdomain must be provided',
- module: MODULE_NAME,
- method: 'checkTenantValidity',
- });
- }
// Get the Tenant
let tenant: Tenant;
if (tenantID) {
@@ -84,7 +71,7 @@ export default class CommonService {
}
}
// Check the redirection
- if (tenant.redirectDomain) {
+ if (tenant?.redirectDomain) {
throw new AppError({
errorCode: StatusCodes.MOVED_PERMANENTLY,
message: ReasonPhrases.MOVED_PERMANENTLY,
@@ -100,8 +87,5 @@ export default class CommonService {
} catch (err) {
next(err);
}
- } else {
- next();
- }
}
}

MODIFY
src/server/rest/v1/service/CompanyService.ts
src/server/rest/v1/service/CompanyService.ts
import { Action, Entity } from '../../../../types/Authorization';
import { NextFunction, Request, Response } from 'express';
+import AppError from '../../../../exception/AppError';
import AuthorizationService from './AuthorizationService';
import BillingStorage from '../../../../storage/mongodb/BillingStorage';
import Company from '../../../../types/Company';
@@ -12,7 +13,6 @@ import Logging from '../../../../utils/Logging';
import { ServerAction } from '../../../../types/Server';
import { StatusCodes } from 'http-status-codes';
import { TenantComponents } from '../../../../types/Tenant';
-import TenantStorage from '../../../../storage/mongodb/TenantStorage';
import Utils from '../../../../utils/Utils';
import UtilsService from './UtilsService';
@@ -55,6 +55,14 @@ export default class CompanyService {
}
public static async handleGetCompanyLogo(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ // Check Tenant
+ if (!req.tenant) {
+ throw new AppError({
+ errorCode: StatusCodes.BAD_REQUEST,
+ message: 'Tenant must be provided',
+ module: MODULE_NAME, method: 'handleGetCompanyLogo', action: action,
+ });
+ }
// Filter
const filteredRequest = CompanyValidatorRest.getInstance().validateCompanyLogoGetReq(req.query);
// Get the Logo

MODIFY
src/server/rest/v1/service/SessionHashService.ts
src/server/rest/v1/service/SessionHashService.ts
@@ -23,7 +23,7 @@ export default class SessionHashService {
// Get Tenant
let tenant: Tenant;
if (tenantID === Constants.DEFAULT_TENANT_ID) {
- tenant = { id: Constants.DEFAULT_TENANT_ID } as Tenant;
+ tenant = Constants.DEFAULT_TENANT_OBJECT;
} else {
tenant = await TenantStorage.getTenant(tenantID);
}

MODIFY
src/server/rest/v1/service/SiteAreaService.ts
src/server/rest/v1/service/SiteAreaService.ts
@@ -21,7 +21,6 @@ import SiteAreaStorage from '../../../../storage/mongodb/SiteAreaStorage';
import SiteAreaValidatorRest from '../validator/SiteAreaValidatorRest';
import SmartChargingFactory from '../../../../integration/smart-charging/SmartChargingFactory';
import { StatusCodes } from 'http-status-codes';
-import TenantStorage from '../../../../storage/mongodb/TenantStorage';
import UserToken from '../../../../types/UserToken';
import Utils from '../../../../utils/Utils';
import UtilsService from './UtilsService';
@@ -162,6 +161,14 @@ export default class SiteAreaService {
}
public static async handleGetSiteAreaImage(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ // Check Tenant
+ if (!req.tenant) {
+ throw new AppError({
+ errorCode: StatusCodes.BAD_REQUEST,
+ message: 'Tenant must be provided',
+ module: MODULE_NAME, method: 'handleGetSiteAreaImage', action: action,
+ });
+ }
// Filter
const filteredRequest = SiteAreaValidatorRest.getInstance().validateSiteAreaGetImageReq(req.query);
// Get it

MODIFY
src/server/rest/v1/service/SiteService.ts
src/server/rest/v1/service/SiteService.ts
@@ -18,7 +18,6 @@ import SiteValidatorRest from '../validator/SiteValidatorRest';
import SitesAdminDynamicAuthorizationDataSource from '../../../../authorization/dynamic-data-source/SitesAdminDynamicAuthorizationDataSource';
import { StatusCodes } from 'http-status-codes';
import { TenantComponents } from '../../../../types/Tenant';
-import TenantStorage from '../../../../storage/mongodb/TenantStorage';
import Utils from '../../../../utils/Utils';
import UtilsService from './UtilsService';
@@ -251,6 +250,14 @@ export default class SiteService {
}
public static async handleGetSiteImage(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ // Check Tenant
+ if (!req.tenant) {
+ throw new AppError({
+ errorCode: StatusCodes.BAD_REQUEST,
+ message: 'Tenant must be provided',
+ module: MODULE_NAME, method: 'handleGetSiteImage', action: action,
+ });
+ }
// This endpoint is not protected, so no need to check user's access
const filteredRequest = SiteValidatorRest.getInstance().validateSiteGetImageReq(req.query);
UtilsService.assertIdIsProvided(action, filteredRequest.ID, MODULE_NAME, 'handleGetSiteImage', req.user);

MODIFY
src/server/rest/v1/service/TenantService.ts
src/server/rest/v1/service/TenantService.ts
@@ -2,7 +2,7 @@ import { Action, Entity } from '../../../../types/Authorization';
import { CryptoSettings, CryptoSettingsType, SettingDB, TechnicalSettings, UserSettings, UserSettingsType } from '../../../../types/Setting';
import { HTTPAuthError, HTTPError } from '../../../../types/HTTPError';
import { NextFunction, Request, Response } from 'express';
-import Tenant, { TenantComponents, TenantLogo } from '../../../../types/Tenant';
+import Tenant, { TenantComponents } from '../../../../types/Tenant';
import User, { UserRole } from '../../../../types/User';
import AppAuthError from '../../../../exception/AppAuthError';
@@ -76,6 +76,14 @@ export default class TenantService {
}
public static async handleGetTenantLogo(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ // Check Tenant
+ if (!req.tenant) {
+ throw new AppError({
+ errorCode: StatusCodes.BAD_REQUEST,
+ message: 'Tenant must be provided',
+ module: MODULE_NAME, method: 'handleGetTenantLogo', action: action,
+ });
+ }
// Get Logo
const tenantLogo = await TenantStorage.getTenantLogo(req.tenant);
let logo = tenantLogo?.logo;

MODIFY
src/utils/Logging.ts
src/utils/Logging.ts
@@ -677,7 +677,8 @@ export default class Logging {
}
// Send error
if (!res.headersSent) {
- const errorCode = exception['params'] ? exception['params']['errorCode'] : HTTPError.GENERAL_ERROR;
+ const errorCode = exception['params'] && exception['params']['errorCode'] ?
+ exception['params']['errorCode'] : HTTPError.GENERAL_ERROR;
res.status(errorCode)
.send({
errorCode,

Redirect simplification (#3676)

* Redirect simplification proposal
Always try to get the tenant in unprotected endpoints
Check that tenant is provided in handleXXX methods

* Fixed undefined error status code returned to HTTP request