Upgate handleGetCompanies logic & solve conflicts
MODIFY
src/authorization/AuthorizationsDefinition.ts
src/authorization/AuthorizationsDefinition.ts
@@ -32,15 +32,23 @@ const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
},
{ resource: Entity.COMPANIES, action: Action.LIST, attributes: [
'id', 'name', 'address', 'logo', 'issuer', 'distanceMeters', 'createdOn', 'lastChangedOn',
- 'createdBy.name', 'createdBy.firstName', 'lastChangedBy.name', 'lastChangedBy.firstName' ] },
+ 'createdBy.name', 'createdBy.firstName', 'lastChangedBy.name', 'lastChangedBy.firstName'
+ ] },
{ resource: Entity.TAGS, action: [Action.LIST, Action.IMPORT], attributes: ['*'] },
{ resource: Entity.TAG, action: [Action.CREATE, Action.UPDATE, Action.DELETE, Action.READ], attributes: ['*'] },
{ resource: Entity.CHARGING_PROFILES, action: Action.LIST, attributes: ['*'] },
{ resource: Entity.CHARGING_PROFILE, action: [Action.READ], attributes: ['*'] },
{ resource: Entity.COMPANY, action: Action.READ, attributes: ['id', 'name', 'issuer', 'logo', 'address'] },
{ resource: Entity.COMPANY, action: [Action.CREATE, Action.UPDATE, Action.DELETE], attributes: ['*'] },
- { resource: Entity.SITES, action: Action.LIST, attributes: ['*'] },
- { resource: Entity.SITE, action: [Action.CREATE, Action.READ, Action.UPDATE, Action.DELETE], attributes: ['*'] },
+ { resource: Entity.SITES, action: Action.LIST, attributes: [
+ 'id', 'name', 'address', 'companyID', 'company.name', 'autoUserSiteAssignment', 'issuer',
+ 'autoUserSiteAssignment', 'distanceMeters', 'public', 'createdOn', 'lastChangedOn',
+ 'createdBy.name', 'createdBy.firstName', 'lastChangedBy.name', 'lastChangedBy.firstName'
+ ] },
+ { resource: Entity.SITE, action: Action.READ, attributes: [
+ 'id', 'name', 'address', 'companyID', 'company.name', 'autoUserSiteAssignment', 'issuer',
+ 'autoUserSiteAssignment', 'distanceMeters', 'public', 'createdOn', 'lastChangedOn'] },
+ { resource: Entity.SITE, action: [Action.CREATE, Action.UPDATE, Action.DELETE], attributes: ['*'] },
{ resource: Entity.SITE_AREAS, action: Action.LIST, attributes: ['*'] },
{ resource: Entity.SITE_AREA, action: [Action.CREATE, Action.READ, Action.UPDATE, Action.DELETE], attributes: ['*'] },
{ resource: Entity.CHARGING_STATIONS, action: [Action.LIST, Action.IN_ERROR], attributes: ['*'] },
@@ -153,8 +161,25 @@ const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
// condition: { Fn: 'EQUALS', args: { 'user': '$.owner' } }
// },
// -----------------------------------------------------------------------------------------------
- { resource: Entity.SITES, action: Action.LIST, attributes: ['*'] },
- { resource: Entity.SITE, action: Action.READ, attributes: ['*'] },
+ { resource: Entity.SITES, action: Action.LIST,
+ condition: {
+ Fn: 'custom:dynamicAuthorizationFilters',
+ args: { filters: ['AssignedSites'] }
+ },
+ attributes: [
+ 'id', 'name', 'address', 'companyID', 'company.name', 'autoUserSiteAssignment', 'issuer',
+ 'autoUserSiteAssignment', 'distanceMeters', 'public', 'createdOn', 'lastChangedOn',
+ ]
+ },
+ { resource: Entity.SITE, action: Action.READ,
+ condition: {
+ Fn: 'custom:dynamicAuthorizationFilters',
+ args: { filters: ['AssignedSites'] }
+ },
+ attributes: [
+ 'id', 'name', 'address', 'companyID', 'company.name', 'autoUserSiteAssignment', 'issuer',
+ 'autoUserSiteAssignment', 'distanceMeters', 'public', 'createdOn', 'lastChangedOn',
+ ] },
{ resource: Entity.SITE_AREAS, action: Action.LIST, attributes: ['*'] },
{ resource: Entity.SITE_AREA, action: Action.READ, attributes: ['*'] },
{ resource: Entity.CHARGING_STATIONS, action: Action.LIST, attributes: ['*'] },
@@ -242,11 +267,18 @@ const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
{ resource: Entity.CAR, action: Action.READ, attributes: ['*'] },
{ resource: Entity.CARS, action: Action.LIST, attributes: ['*'] },
{ resource: Entity.COMPANIES, action: Action.LIST, attributes: [
- 'id', 'name', 'address', 'logo', 'issuer', 'distanceMeters', 'createdOn', 'lastChangedOn'] },
+ 'id', 'name', 'address', 'logo', 'issuer', 'distanceMeters', 'createdOn', 'lastChangedOn'
+ ] },
{ resource: Entity.COMPANY, action: Action.READ, attributes: [
'id', 'name', 'issuer', 'logo', 'address'] },
- { resource: Entity.SITES, action: Action.LIST, attributes: ['*'] },
- { resource: Entity.SITE, action: Action.READ, attributes: ['*'] },
+ { resource: Entity.SITES, action: Action.LIST, attributes: [
+ 'id', 'name', 'address', 'companyID', 'company.name', 'autoUserSiteAssignment', 'issuer',
+ 'autoUserSiteAssignment', 'distanceMeters', 'public', 'createdOn', 'lastChangedOn',
+ ] },
+ { resource: Entity.SITE, action: Action.READ, attributes: [
+ 'id', 'name', 'address', 'companyID', 'company.name', 'autoUserSiteAssignment', 'issuer',
+ 'autoUserSiteAssignment', 'distanceMeters', 'public', 'createdOn', 'lastChangedOn',
+ ] },
{ resource: Entity.SITE_AREAS, action: Action.LIST, attributes: ['*'] },
{ resource: Entity.SITE_AREA, action: Action.READ, attributes: ['*'] },
{ resource: Entity.CHARGING_STATIONS, action: Action.LIST, attributes: ['*'] },
@@ -281,7 +313,12 @@ const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
{ resource: Entity.USER, action: [Action.READ], attributes: ['*'] },
{ resource: Entity.USERS_SITES, action: Action.LIST, attributes: ['*'] },
{ resource: Entity.USERS_SITES, action: [Action.UNASSIGN], attributes: ['*'] },
- { resource: Entity.SITE, action: [Action.UPDATE], attributes: ['*'] },
+ { resource: Entity.SITE, action: [Action.UPDATE],
+ condition: {
+ Fn: 'custom:dynamicAuthorizationFilters',
+ args: { filters: ['SitesAdmin'] }
+ },
+ attributes: ['*'] },
{ resource: Entity.SITE_AREA, action: [Action.CREATE, Action.UPDATE, Action.DELETE], attributes: ['*'] },
{
resource: Entity.CHARGING_STATION,

MODIFY
src/authorization/DynamicAuthorizationFactory.ts
src/authorization/DynamicAuthorizationFactory.ts
@@ -2,6 +2,8 @@ import { DynamicAuthorizationDataSourceData, DynamicAuthorizationDataSourceName,
import AssignedSitesCompaniesDynamicAuthorizationDataSource from './dynamic-data-source/AssignedSitesCompaniesDynamicAuthorizationDataSource';
import AssignedSitesCompaniesDynamicAuthorizationFilter from './dynamic-filters/AssignedSitesCompaniesDynamicAuthorizationFilter';
+import AssignedSitesDynamicAuthorizationDataSource from './dynamic-data-source/AssignedSitesDynamicAuthorizationDataSource';
+import AssignedSitesDynamicAuthorizationFilter from './dynamic-filters/AssignedSitesDynamicAuthorizationFilter';
import DynamicAuthorizationDataSource from './DynamicAuthorizationDataSource';
import DynamicAuthorizationFilter from './DynamicAuthorizationFilter';
import Tenant from '../types/Tenant';
@@ -16,6 +18,10 @@ export default class DynamicAuthorizationFactory {
switch (filter) {
case DynamicAuthorizationFilterName.ASSIGNED_SITES_COMPANIES:
dynamicFilter = new AssignedSitesCompaniesDynamicAuthorizationFilter(tenant, userToken);
+ break;
+ case DynamicAuthorizationFilterName.ASSIGNED_SITES:
+ dynamicFilter = new AssignedSitesDynamicAuthorizationFilter(tenant, userToken);
+ break;
}
// Init Data Source
if (dynamicFilter) {
@@ -50,6 +56,8 @@ export default class DynamicAuthorizationFactory {
switch (dataSource) {
case DynamicAuthorizationDataSourceName.ASSIGNED_SITES_COMPANIES:
return new AssignedSitesCompaniesDynamicAuthorizationDataSource(tenant, user);
+ case DynamicAuthorizationDataSourceName.ASSIGNED_SITES:
+ return new AssignedSitesDynamicAuthorizationDataSource(tenant, user);
}
}
}

ADD
src/authorization/dynamic-data-source/AssignedSitesDynamicAuthorizationDataSource.ts
+import { AssignedSitesDynamicAuthorizationDataSourceData, 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 AssignedSitesDynamicAuthorizationDataSource
+ extends DynamicAuthorizationDataSource<AssignedSitesDynamicAuthorizationDataSourceData> {
+
+ public getName(): DynamicAuthorizationDataSourceName {
+ return DynamicAuthorizationDataSourceName.ASSIGNED_SITES;
+ }
+
+ public async loadData(): Promise<void> {
+ const assignedSitesCompaniesData: AssignedSitesDynamicAuthorizationDataSourceData = {};
+ // Get Site IDs from Site Admin flag
+ assignedSitesCompaniesData.siteIDs = await this.getAssignedSiteIDs();
+ // Set
+ this.setData(assignedSitesCompaniesData);
+ }
+
+ private async getAssignedSiteIDs(): Promise<string[]> {
+ // Get the Company IDs of the assigned Sites
+ const sites = await SiteStorage.getSites(this.tenant.id,
+ {
+ userID: this.userToken.id,
+ issuer: true,
+ }, Constants.DB_PARAMS_MAX_LIMIT,
+ ['id']
+ );
+ return _.uniq(_.map(sites.result, 'id'));
+ }
+}

ADD
src/authorization/dynamic-filters/AssignedSitesDynamicAuthorizationFilter.ts
+import { AuthorizationFilter, DynamicAuthorizationDataSourceName, Entity } from '../../types/Authorization';
+
+import AssignedSitesDynamicAuthorizationDataSource from '../dynamic-data-source/AssignedSitesDynamicAuthorizationDataSource';
+// import AssignedSitesCompaniesDynamicAuthorizationDataSource from '../dynamic-data-source/AssignedSitesCompaniesDynamicAuthorizationDataSource';
+import DynamicAuthorizationFilter from '../DynamicAuthorizationFilter';
+import Utils from '../../utils/Utils';
+
+export default class AssignedSitesDynamicAuthorizationFilter extends DynamicAuthorizationFilter {
+ public processFilter(authorizationFilters: AuthorizationFilter, extraFilters: Record<string, any>): void {
+ // Get Site IDs
+ const assignedSitesDataSource = this.getDataSource(
+ DynamicAuthorizationDataSourceName.ASSIGNED_SITES) as AssignedSitesDynamicAuthorizationDataSource;
+ const { siteIDs } = assignedSitesDataSource.getData();
+ // Check
+ if (!Utils.isEmptyArray(siteIDs)) {
+ // Force the filter
+ authorizationFilters.filters.siteIDs = siteIDs;
+ // Check if filter is provided
+ if (Utils.objectHasProperty(extraFilters, 'SiteID') &&
+ !Utils.isNullOrUndefined(extraFilters['SiteID'])) {
+ const filteredSiteIDs: string[] = extraFilters['SiteID'].split('|');
+ // Override
+ authorizationFilters.filters.siteIDs = filteredSiteIDs.filter(
+ (siteID) => authorizationFilters.filters.siteIDs.includes(siteID));
+ }
+ }
+ if (!Utils.isEmptyArray(authorizationFilters.filters.siteIDs)) {
+ authorizationFilters.authorized = true;
+ }
+ }
+
+ public getApplicableEntities(): Entity[] {
+ return [
+ Entity.SITES
+ ];
+ }
+
+ public getApplicableDataSources(): DynamicAuthorizationDataSourceName[] {
+ return [
+ DynamicAuthorizationDataSourceName.ASSIGNED_SITES
+ ];
+ }
+}

MODIFY
src/server/rest/v1/service/AuthorizationService.ts
src/server/rest/v1/service/AuthorizationService.ts
@@ -76,22 +76,20 @@ export default class AuthorizationService {
return authorizationFilters;
}
- public static async addSitesAuthorizations(tenant: Tenant, userToken: UserToken, sites: SiteDataResult): Promise<void> {
- // Get Site Admins
- const siteAdminIDs = await AuthorizationService.getSiteAdminSiteIDs(tenant.id, userToken);
+ public static async addSitesAuthorizations(tenant: Tenant, userToken: UserToken, sites: SiteDataResult, authorizationFilter: AuthorizationFilter): Promise<void> {
// Add canCreate flag to root
- sites.canCreate = await Authorizations.canCreateSite(userToken);
+ sites.canCreate = await AuthorizationService.canPerformAuthorizationAction(tenant, userToken, Entity.COMPANY, Action.CREATE, authorizationFilter);
+
// Enrich
for (const site of sites.result) {
- await AuthorizationService.addSiteAuthorizations(tenant, userToken, site, siteAdminIDs);
+ await AuthorizationService.addSiteAuthorizations(tenant, userToken, site, authorizationFilter);
}
}
- public static async addSiteAuthorizations(tenant: Tenant, userToken: UserToken, site: Site, siteAdminIDs?: string[]): Promise<void> {
+ public static async addSiteAuthorizations(tenant: Tenant, userToken: UserToken, site: Site, authorizationFilter: AuthorizationFilter): Promise<void> {
+ // todo: remove when assign actions are in place
// Get Site Admins
- if (Utils.isEmptyArray(siteAdminIDs)) {
- siteAdminIDs = await AuthorizationService.getSiteAdminSiteIDs(tenant.id, userToken);
- }
+ const siteAdminIDs = await AuthorizationService.getSiteAdminSiteIDs(tenant.id, userToken);
// Enrich
if (!site.issuer) {
site.canRead = true;
@@ -99,9 +97,10 @@ export default class AuthorizationService {
site.canDelete = false;
} else {
const isSiteAdmin = siteAdminIDs.includes(site.id) || (userToken.role === UserRole.ADMIN);
- site.canRead = await Authorizations.canReadSite(userToken);
- site.canDelete = await Authorizations.canDeleteSite(userToken);
- site.canUpdate = await Authorizations.canUpdateSite(userToken) && isSiteAdmin;
+ site.canRead = await AuthorizationService.canPerformAuthorizationAction(tenant, userToken, Entity.COMPANY, Action.READ, authorizationFilter);
+ site.canDelete = await AuthorizationService.canPerformAuthorizationAction(tenant, userToken, Entity.COMPANY, Action.DELETE, authorizationFilter);
+ site.canUpdate = await AuthorizationService.canPerformAuthorizationAction(tenant, userToken, Entity.COMPANY, Action.UPDATE, authorizationFilter);
+ // todo: change when assign actions are in place
site.canAssignUsers = await Authorizations.canAssignUsersSites(userToken);
site.canUnassignUsers = await Authorizations.canUnassignUsersSites(userToken) && isSiteAdmin;
}
@@ -112,35 +111,12 @@ export default class AuthorizationService {
const authorizationFilters: AuthorizationFilter = {
filters: {},
dataSources: new Map(),
- projectFields: [
- 'id', 'name', 'address', 'companyID', 'company.name', 'autoUserSiteAssignment', 'issuer',
- 'autoUserSiteAssignment', 'distanceMeters', 'public', 'createdOn', 'lastChangedOn',
- ],
- authorized: userToken.role === UserRole.ADMIN,
+ projectFields: [],
+ authorized: false,
};
- // Check static auth
- if (!await Authorizations.canListSites(userToken)) {
- throw new AppAuthError({
- errorCode: HTTPAuthError.FORBIDDEN,
- user: userToken,
- action: Action.LIST, entity: Entity.SITES,
- module: MODULE_NAME, method: 'checkAndGetSitesAuthorizationFilters'
- });
- }
- // Add user info
- if (await Authorizations.canListUsers(userToken)) {
- authorizationFilters.projectFields.push(
- 'createdBy.name', 'createdBy.firstName', 'lastChangedBy.name', 'lastChangedBy.firstName');
- }
- // Filter projected fields
- authorizationFilters.projectFields = AuthorizationService.filterProjectFields(
- authorizationFilters.projectFields, filteredRequest.ProjectFields);
- // Not an Admin?
- if (userToken.role !== UserRole.ADMIN) {
- // Check assigned Sites
- await AuthorizationService.checkAssignedSites(
- tenant, userToken, filteredRequest, authorizationFilters);
- }
+ // Check static & dynamic authorization
+ await this.canPerformAuthorizationAction(
+ tenant, userToken, Entity.SITES, Action.LIST, authorizationFilters, filteredRequest);
return authorizationFilters;
}

MODIFY
src/server/rest/v1/service/SiteService.ts
src/server/rest/v1/service/SiteService.ts
@@ -281,7 +281,8 @@ export default class SiteService {
withImage: true,
}, true);
// Add authorizations
- await AuthorizationService.addSiteAuthorizations(req.tenant, req.user, site);
+ // todo: add back
+ // await AuthorizationService.addSiteAuthorizations(req.tenant, req.user, site);
// Return
res.json(site);
next();
@@ -294,9 +295,9 @@ export default class SiteService {
// Filter request
const filteredRequest = SiteSecurity.filterSitesRequest(req.query);
// Check dynamic auth
- const authorizationSiteFilters = await AuthorizationService.checkAndGetSitesAuthorizationFilters(
+ const authorizationSitesFilter = await AuthorizationService.checkAndGetSitesAuthorizationFilters(
req.tenant, req.user, filteredRequest);
- if (!authorizationSiteFilters.authorized) {
+ if (!authorizationSitesFilter.authorized) {
UtilsService.sendEmptyDataResult(res, next);
return;
}
@@ -313,7 +314,7 @@ export default class SiteService {
withAvailableChargingStations: filteredRequest.WithAvailableChargers,
locCoordinates: filteredRequest.LocCoordinates,
locMaxDistanceMeters: filteredRequest.LocMaxDistanceMeters,
- ...authorizationSiteFilters.filters
+ ...authorizationSitesFilter.filters
},
{
limit: filteredRequest.Limit,
@@ -321,10 +322,10 @@ export default class SiteService {
sort: filteredRequest.SortFields,
onlyRecordCount: filteredRequest.OnlyRecordCount
},
- authorizationSiteFilters.projectFields
+ authorizationSitesFilter.projectFields
);
// Add Auth flags
- await AuthorizationService.addSitesAuthorizations(req.tenant, req.user, sites as SiteDataResult);
+ await AuthorizationService.addSitesAuthorizations(req.tenant, req.user, sites as SiteDataResult, authorizationSitesFilter);
// Return
res.json(sites);
next();

MODIFY
src/types/Authorization.ts
src/types/Authorization.ts
@@ -182,10 +182,12 @@ export interface SiteAuthorizationActions extends AuthorizationActions {
export enum DynamicAuthorizationFilterName {
ASSIGNED_SITES_COMPANIES = 'AssignedSitesCompanies',
+ ASSIGNED_SITES = 'AssignedSites',
}
export enum DynamicAuthorizationDataSourceName {
ASSIGNED_SITES_COMPANIES = 'AssignedSitesCompanies',
+ ASSIGNED_SITES = 'AssignedSites',
}
export interface DynamicAuthorizationDataSourceData {}
@@ -193,3 +195,7 @@ export interface DynamicAuthorizationDataSourceData {}
export interface AssignedSitesCompaniesDynamicAuthorizationDataSourceData extends DynamicAuthorizationDataSourceData {
companyIDs?: string[];
}
+
+export interface AssignedSitesDynamicAuthorizationDataSourceData extends DynamicAuthorizationDataSourceData {
+ siteIDs?: string[];
+}

Upgate handleGetCompanies logic & solve conflicts