Data source first integration
MODIFY
package-lock.json
package-lock.json
{
"name": "ev-server",
- "version": "2.4.59",
+ "version": "2.4.60",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

MODIFY
package.json
package.json
{
"name": "ev-server",
- "version": "2.4.59",
+ "version": "2.4.60",
"engines": {
"node": "14.x.x",
"npm": "6.x.x"

ADD
src/authorization/DynamicAuthorizationDataSource.ts
+import { DynamicAuthorizationDataSourceData, DynamicAuthorizationDataSourceName } from '../types/Authorization';
+
+export default abstract class DynamicAuthorizationDataSource<T extends DynamicAuthorizationDataSourceData> {
+ protected tenantID: string;
+ protected userID: string;
+ private dataSourceData: T;
+
+ public constructor(tenantID: string, userID: string) {
+ this.tenantID = tenantID;
+ this.userID = userID;
+ }
+
+ public setData(dataSourceData: T): void {
+ this.dataSourceData = dataSourceData;
+ }
+
+ public getData(): T {
+ return this.dataSourceData;
+ }
+
+ public abstract getName(): DynamicAuthorizationDataSourceName;
+
+ public abstract loadData(): Promise<void>;
+}

ADD
src/authorization/DynamicAuthorizationFactory.ts
+import { DynamicAuthorizationDataSourceData, DynamicAuthorizationDataSourceName, DynamicAuthorizationFilterName } from '../types/Authorization';
+
+import AssignedSitesCompaniesDynamicAuthorizationDataSource from './dynamic-data-source/AssignedSitesCompaniesDynamicAuthorizationDataSource';
+import AssignedSitesCompaniesDynamicAuthorizationFilter from './dynamic-filters/AssignedSitesCompaniesDynamicAuthorizationFilter';
+import DynamicAuthorizationDataSource from './DynamicAuthorizationDataSource';
+import DynamicAuthorizationFilter from './DynamicAuthorizationFilter';
+
+export default class DynamicAuthorizationFactory {
+ public static async getDynamicFilter(tenantID: string, userID: string,
+ filter: DynamicAuthorizationFilterName): Promise<DynamicAuthorizationFilter> {
+ let dynamicFilter: DynamicAuthorizationFilter;
+ switch (filter) {
+ case DynamicAuthorizationFilterName.ASSIGNED_SITES_COMPANIES:
+ dynamicFilter = new AssignedSitesCompaniesDynamicAuthorizationFilter(tenantID, userID);
+ }
+ // Init Data Source
+ if (dynamicFilter) {
+ await DynamicAuthorizationFactory.initFilterDataSources(tenantID, userID, dynamicFilter);
+ }
+ return dynamicFilter;
+ }
+
+ private static async initFilterDataSources(tenantID: string, userID: string,
+ dynamicFilter: DynamicAuthorizationFilter): Promise<void> {
+ // Get Data Source
+ const dataSourceNames = dynamicFilter.getApplicableDataSources();
+ for (const dataSourceName of dataSourceNames) {
+ // Create the data source
+ const dataSource = DynamicAuthorizationFactory.getDynamicDataSource(
+ tenantID, userID, dataSourceName);
+ // Load data
+ await dataSource.loadData();
+ // Set
+ dynamicFilter.setDataSource(dataSourceName, dataSource);
+ }
+ }
+
+ private static getDynamicDataSource(tenantID: string, userID: string,
+ dataSource: DynamicAuthorizationDataSourceName): DynamicAuthorizationDataSource<DynamicAuthorizationDataSourceData> {
+ switch (dataSource) {
+ case DynamicAuthorizationDataSourceName.ASSIGNED_SITES_COMPANIES:
+ return new AssignedSitesCompaniesDynamicAuthorizationDataSource(tenantID, userID);
+ }
+ }
+}

MODIFY
src/authorization/DynamicAuthorizationFilter.ts
src/authorization/DynamicAuthorizationFilter.ts
-import { AuthorizationFilter, Entity } from '../types/Authorization';
+import { AuthorizationFilter, DynamicAuthorizationDataSourceData, DynamicAuthorizationDataSourceName, Entity } from '../types/Authorization';
-import Tenant from '../types/Tenant';
-import UserToken from '../types/UserToken';
+import DynamicAuthorizationDataSource from './DynamicAuthorizationDataSource';
-export default interface DynamicAuthorizationFilter {
- processFilter(tenant: Tenant, userToken: UserToken, authorizationFilters: AuthorizationFilter,
- extraFilters: Record<string, any>): Promise<void>;
+export default abstract class DynamicAuthorizationFilter {
+ protected tenantID: string;
+ protected userID: string;
+ private dataSources: Map<DynamicAuthorizationDataSourceName, DynamicAuthorizationDataSource<DynamicAuthorizationDataSourceData>> = new Map();
- getEntities(): Entity[];
+ public constructor(tenantID: string, userID: string) {
+ this.tenantID = tenantID;
+ this.userID = userID;
+ }
+
+ public getDataSources(): DynamicAuthorizationDataSource<DynamicAuthorizationDataSourceData>[] {
+ return Array.from(this.dataSources.values());
+ }
+
+ public getDataSource(dataSourceName: DynamicAuthorizationDataSourceName): DynamicAuthorizationDataSource<DynamicAuthorizationDataSourceData> {
+ return this.dataSources.get(dataSourceName);
+ }
+
+ public setDataSource(dataSourceName: DynamicAuthorizationDataSourceName,
+ dataSource: DynamicAuthorizationDataSource<DynamicAuthorizationDataSourceData>): void {
+ this.dataSources.set(dataSourceName, dataSource);
+ }
+
+ public abstract processFilter(
+ authorizationFilters: AuthorizationFilter, extraFilters: Record<string, any>): void;
+
+ public abstract getApplicableEntities(): Entity[];
- getDataSources(): string[];
+ public abstract getApplicableDataSources(): DynamicAuthorizationDataSourceName[];
}

DELETE
src/authorization/DynamicAuthorizationFilterFactory.ts
-import AssignedSitesCompaniesDynamicAuthorizationFilter from './dynamic-filters/AssignedSitesCompaniesDynamicAuthorizationFilter';
-import DynamicAuthorizationFilter from './DynamicAuthorizationFilter';
-import { DynamicAuthorizationFilters } from '../types/Authorization';
-
-export default class DynamicAuthorizationFilterFactory {
- public static getDynamicFilter(filter: DynamicAuthorizationFilters): DynamicAuthorizationFilter {
- switch (filter) {
- case DynamicAuthorizationFilters.ASSIGNED_SITES_COMPANIES:
- return new AssignedSitesCompaniesDynamicAuthorizationFilter();
- }
- }
-}

ADD
src/authorization/dynamic-data-source/AssignedSitesCompaniesDynamicAuthorizationDataSource.ts
+import { AssignedSitesCompaniesDynamicAuthorizationDataSourceData, DynamicAuthorizationDataSourceName } from '../../types/Authorization';
+
+import Constants from '../../utils/Constants';
+import DynamicAuthorizationDataSource from '../DynamicAuthorizationDataSource';
+import SiteStorage from '../../storage/mongodb/SiteStorage';
+import _ from 'lodash';
+
+export default class AssignedSitesCompaniesDynamicAuthorizationDataSource
+ extends DynamicAuthorizationDataSource<AssignedSitesCompaniesDynamicAuthorizationDataSourceData> {
+
+ public getName(): DynamicAuthorizationDataSourceName {
+ return DynamicAuthorizationDataSourceName.ASSIGNED_SITES_COMPANIES;
+ }
+
+ public async loadData(): Promise<void> {
+ const assignedSitesCompaniesData: AssignedSitesCompaniesDynamicAuthorizationDataSourceData = {};
+ // Get Company IDs from Site Admin flag
+ assignedSitesCompaniesData.companyIDs = await this.getAssignedSitesCompanyIDs();
+ // Set
+ this.setData(assignedSitesCompaniesData);
+ }
+
+ private async getAssignedSitesCompanyIDs(): Promise<string[]> {
+ // Get the Company IDs of the assigned Sites
+ const sites = await SiteStorage.getSites(this.tenantID,
+ {
+ userID: this.userID,
+ issuer: true,
+ }, Constants.DB_PARAMS_MAX_LIMIT,
+ ['companyID']
+ );
+ return _.uniq(_.map(sites.result, 'companyID'));
+ }
+}

MODIFY
src/authorization/dynamic-filters/AssignedSitesCompaniesDynamicAuthorizationFilter.ts
src/authorization/dynamic-filters/AssignedSitesCompaniesDynamicAuthorizationFilter.ts
-import { AuthorizationFilter, Entity } from '../../types/Authorization';
+import { AuthorizationFilter, DynamicAuthorizationDataSourceName, Entity } from '../../types/Authorization';
-import AuthorizationService from '../../server/rest/v1/service/AuthorizationService';
+import AssignedSitesCompaniesDynamicAuthorizationDataSource from '../dynamic-data-source/AssignedSitesCompaniesDynamicAuthorizationDataSource';
import DynamicAuthorizationFilter from '../DynamicAuthorizationFilter';
-import Tenant from '../../types/Tenant';
-import TenantComponents from '../../types/TenantComponents';
-import UserToken from '../../types/UserToken';
import Utils from '../../utils/Utils';
-export default class AssignedSitesCompaniesDynamicAuthorizationFilter implements DynamicAuthorizationFilter {
- // TODO: Process the assigned company IDs from the extraFilters (do not call the DB)
- public async processFilter(tenant: Tenant, userToken: UserToken,
- authorizationFilters: AuthorizationFilter, extraFilters: Record<string, any>): Promise<void> {
- if (Utils.isTenantComponentActive(tenant, TenantComponents.ORGANIZATION)) {
- // Get Company IDs from Site Admin flag
- const companyIDs = await AuthorizationService.getAssignedSitesCompanyIDs(tenant.id, userToken);
+export default class AssignedSitesCompaniesDynamicAuthorizationFilter extends DynamicAuthorizationFilter {
+ public processFilter(authorizationFilters: AuthorizationFilter, extraFilters: Record<string, any>): void {
+ // Get Company IDs
+ const assignedSitesCompaniesDataSource = this.getDataSource(
+ DynamicAuthorizationDataSourceName.ASSIGNED_SITES_COMPANIES) as AssignedSitesCompaniesDynamicAuthorizationDataSource;
+ const { companyIDs } = assignedSitesCompaniesDataSource.getData();
+ // Check
if (!Utils.isEmptyArray(companyIDs)) {
// Force the filter
authorizationFilters.filters.companyIDs = companyIDs;
@@ -29,16 +26,18 @@ export default class AssignedSitesCompaniesDynamicAuthorizationFilter implements
if (!Utils.isEmptyArray(authorizationFilters.filters.companyIDs)) {
authorizationFilters.authorized = true;
}
- } else {
- authorizationFilters.authorized = true;
- }
}
- public getEntities(): Entity[] {
- return [Entity.COMPANY, Entity.COMPANIES];
+ public getApplicableEntities(): Entity[] {
+ return [
+ Entity.COMPANY,
+ Entity.COMPANIES
+ ];
}
- getDataSources(): string[] {
- return [];
+ public getApplicableDataSources(): DynamicAuthorizationDataSourceName[] {
+ return [
+ DynamicAuthorizationDataSourceName.ASSIGNED_SITES_COMPANIES
+ ];
}
}

MODIFY
src/server/rest/v1/service/AuthorizationService.ts
src/server/rest/v1/service/AuthorizationService.ts
@@ -12,7 +12,7 @@ import Authorizations from '../../../../authorization/Authorizations';
import ChargingStationStorage from '../../../../storage/mongodb/ChargingStationStorage';
import Company from '../../../../types/Company';
import Constants from '../../../../utils/Constants';
-import DynamicAuthorizationFilterFactory from '../../../../authorization/DynamicAuthorizationFilterFactory';
+import DynamicAuthorizationFactory from '../../../../authorization/DynamicAuthorizationFactory';
import { HTTPAuthError } from '../../../../types/HTTPError';
import { HttpAssetsRequest } from '../../../../types/requests/HttpAssetRequest';
import HttpByIDRequest from '../../../../types/requests/HttpByIDRequest';
@@ -640,19 +640,6 @@ export default class AuthorizationService {
return authorizationFilters;
}
- public static async getAssignedSitesCompanyIDs(tenantID: string, userToken: UserToken, siteID?: string): Promise<string[]> {
- // Get the Company IDs of the assigned Sites
- const sites = await SiteStorage.getSites(tenantID,
- {
- siteIDs: siteID ? [siteID] : null,
- userID: userToken.id,
- issuer: true,
- }, Constants.DB_PARAMS_MAX_LIMIT,
- ['companyID']
- );
- return _.uniq(_.map(sites.result, 'companyID'));
- }
-
public static async getSiteAdminSiteIDs(tenantID: string, userToken: UserToken): Promise<string[]> {
// Get the Sites where the user is Site Admin
const userSites = await UserStorage.getUserSites(tenantID,
@@ -828,7 +815,7 @@ export default class AuthorizationService {
// Reset to false
authorizationFilters.authorized = false;
// Get the filter
- const dynamicFilter = DynamicAuthorizationFilterFactory.getDynamicFilter(filter);
+ const dynamicFilter = await DynamicAuthorizationFactory.getDynamicFilter(tenant.id, userToken.id, filter);
if (!dynamicFilter) {
// Filter not found -> Not authorized (all auth filter MUST work)
throw new AppAuthError({
@@ -839,7 +826,7 @@ export default class AuthorizationService {
});
}
// Process the filter
- await dynamicFilter.processFilter(tenant, userToken, authorizationFilters, extraFilters);
+ dynamicFilter.processFilter(authorizationFilters, extraFilters);
// Check
if (!authorizationFilters.authorized) {
break;

MODIFY
src/types/Authorization.ts
src/types/Authorization.ts
@@ -162,7 +162,7 @@ export interface AuthorizationContext {
companies?: string[];
asset?: string;
assets?: string[];
- filters?: DynamicAuthorizationFilters[];
+ filters?: DynamicAuthorizationFilterName[];
}
export interface AuthorizationActions {
@@ -172,6 +172,16 @@ export interface AuthorizationActions {
canDelete?: boolean;
}
-export enum DynamicAuthorizationFilters {
+export enum DynamicAuthorizationFilterName {
ASSIGNED_SITES_COMPANIES = 'AssignedSitesCompanies',
}
+
+export enum DynamicAuthorizationDataSourceName {
+ ASSIGNED_SITES_COMPANIES = 'AssignedSitesCompanies',
+}
+
+export interface DynamicAuthorizationDataSourceData {}
+
+export interface AssignedSitesCompaniesDynamicAuthorizationDataSourceData extends DynamicAuthorizationDataSourceData {
+ companyIDs?: string[];
+}

Data source first integration