Add ExcludeAction dynamic filter
MODIFY
src/authorization/AuthorizationsDefinition.ts
src/authorization/AuthorizationsDefinition.ts
@@ -131,7 +131,8 @@ const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
resource: Entity.COMPANY,
action: [
Action.CREATE, Action.UPDATE, Action.DELETE
- ] },
+ ]
+ },
{
resource: Entity.SITES, action: Action.LIST,
attributes: [
@@ -521,7 +522,7 @@ const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
args: { filters: ['OwnUser'] }
},
attributes: [
- 'userID', 'active', 'ocpiToken', 'description', 'visualID', 'issuer', 'default',
+ 'userID', 'active', 'description', 'visualID', 'issuer', 'default',
'createdOn', 'lastChangedOn'
],
},
@@ -540,33 +541,38 @@ const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
resource: Entity.TAGS, action: Action.UNASSIGN,
condition: {
Fn: 'custom:dynamicAuthorizations',
- args: { filters: ['OwnUser'] }
+ args: {
+ filters: ['OwnUser'],
+ asserts: ['OwnUser']
+ }
}
},
{
resource: Entity.TAG, action: Action.UNASSIGN,
condition: {
Fn: 'custom:dynamicAuthorizations',
- args: { filters: ['OwnUser'] }
+ args: {
+ filters: ['OwnUser'],
+ asserts: ['OwnUser']
+ }
}
},
{
resource: Entity.TAG, action: Action.UPDATE_BY_VISUAL_ID,
condition: {
Fn: 'custom:dynamicAuthorizations',
- args: { filters: ['OwnUser'] }
+ args: {
+ filters: ['OwnUser'],
+ asserts: ['OwnUser']
+ }
}
},
{
resource: Entity.TAG, action: Action.ASSIGN,
attributes: [
- 'userID', 'active', 'ocpiToken', 'description', 'visualID', 'issuer', 'default',
+ 'userID', 'description', 'visualID', 'default',
'user.name', 'user.firstName', 'user.email', 'createdOn', 'lastChangedOn'
- ],
- condition: {
- Fn: 'custom:dynamicAuthorizations',
- args: { filters: ['OwnUser'] }
- }
+ ]
},
{
resource: Entity.CHARGING_STATION,
@@ -731,6 +737,20 @@ const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
'basic': {}
},
grants: [
+ {
+ resource: Entity.TAG, action: [Action.ASSIGN, Action.UNASSIGN, Action.UPDATE_BY_VISUAL_ID],
+ condition: {
+ Fn: 'custom:dynamicAuthorizations',
+ args: { filters: ['ExcludeAction'] }
+ }
+ },
+ {
+ resource: Entity.TAGS, action: [Action.UNASSIGN],
+ condition: {
+ Fn: 'custom:dynamicAuthorizations',
+ args: { filters: ['ExcludeAction'] }
+ }
+ },
{
resource: Entity.USERS, action: Action.LIST,
condition: {

MODIFY
src/authorization/DynamicAuthorizationFactory.ts
src/authorization/DynamicAuthorizationFactory.ts
@@ -7,6 +7,8 @@ import AssignedSitesDynamicAuthorizationFilter from './dynamic-filters/AssignedS
import DynamicAuthorizationAssert from './DynamicAuthorizationAssert';
import DynamicAuthorizationDataSource from './DynamicAuthorizationDataSource';
import DynamicAuthorizationFilter from './DynamicAuthorizationFilter';
+import ExcludeActionDynamicAuthorizationDataSource from './dynamic-data-source/ExcludeActionDynamicAuthorizationDataSource';
+import ExcludeActionDynamicAuthorizationFilter from './dynamic-filters/ExcludeActionDynamicAuthorizationFilter';
import LocalIssuerDynamicAuthorizationFilter from './dynamic-filters/LocalIssuerDynamicAuthorizationFilter';
import OwnUserDynamicAuthorizationAssert from './dynamic-assert/OwnUserDynamicAuthorizationAssert';
import OwnUserDynamicAuthorizationDataSource from './dynamic-data-source/OwnUserDynamicAuthorizationDataSource';
@@ -48,6 +50,9 @@ export default class DynamicAuthorizationFactory {
case DynamicAuthorizationFilterName.OWN_USER:
dynamicFilter = new OwnUserDynamicAuthorizationFilter(tenant, userToken, negateFilter);
break;
+ case DynamicAuthorizationFilterName.EXCLUDE_ACTION:
+ dynamicFilter = new ExcludeActionDynamicAuthorizationFilter(tenant, userToken, negateFilter);
+ break;
case DynamicAuthorizationFilterName.LOCAL_ISSUER:
dynamicFilter = new LocalIssuerDynamicAuthorizationFilter(tenant, userToken, negateFilter);
break;
@@ -113,6 +118,8 @@ export default class DynamicAuthorizationFactory {
return new AssignedSitesDynamicAuthorizationDataSource(tenant, user);
case DynamicAuthorizationDataSourceName.OWN_USER:
return new OwnUserDynamicAuthorizationDataSource(tenant, user);
+ case DynamicAuthorizationDataSourceName.EXCLUDE_ACTION:
+ return new ExcludeActionDynamicAuthorizationDataSource(tenant, user);
}
}
}

ADD
src/authorization/dynamic-data-source/ExcludeActionDynamicAuthorizationDataSource.ts
+import { DynamicAuthorizationDataSourceData, DynamicAuthorizationDataSourceName } from '../../types/Authorization';
+
+import DynamicAuthorizationDataSource from '../DynamicAuthorizationDataSource';
+
+export default class ExcludeActionDynamicAuthorizationDataSource
+ extends DynamicAuthorizationDataSource<DynamicAuthorizationDataSourceData> {
+
+ public getName(): DynamicAuthorizationDataSourceName {
+ return DynamicAuthorizationDataSourceName.EXCLUDE_ACTION;
+ }
+
+ public async loadData(): Promise<void> {
+ const sourceData: DynamicAuthorizationDataSourceData = {};
+ // Set
+ this.setData(sourceData);
+ return Promise.resolve();
+ }
+}

ADD
src/authorization/dynamic-filters/ExcludeActionDynamicAuthorizationFilter.ts
+import { AuthorizationFilter, DynamicAuthorizationDataSourceName, Entity } from '../../types/Authorization';
+
+import DynamicAuthorizationFilter from '../DynamicAuthorizationFilter';
+
+export default class ExcludeActionDynamicAuthorizationFilter extends DynamicAuthorizationFilter {
+ public processFilter(authorizationFilters: AuthorizationFilter, extraFilters: Record<string, any>): void {
+ authorizationFilters.authorized = false;
+ }
+
+ public getApplicableEntities(): Entity[] {
+ return [
+ Entity.TAGS
+ ];
+ }
+
+ public getApplicableDataSources(): DynamicAuthorizationDataSourceName[] {
+ return [
+ DynamicAuthorizationDataSourceName.EXCLUDE_ACTION
+ ];
+ }
+}

MODIFY
src/server/rest/v1/service/AuthorizationService.ts
src/server/rest/v1/service/AuthorizationService.ts
@@ -8,6 +8,7 @@ import { HttpCompaniesRequest, HttpCompanyRequest } from '../../../../types/requ
import { HttpSiteAssignUsersRequest, HttpSiteRequest, HttpSiteUsersRequest } from '../../../../types/requests/HttpSiteRequest';
import { HttpTagRequest, HttpTagsRequest } from '../../../../types/requests/HttpTagRequest';
import { HttpUserAssignSitesRequest, HttpUserRequest, HttpUserSitesRequest, HttpUsersRequest } from '../../../../types/requests/HttpUserRequest';
+import Tenant, { TenantComponents } from '../../../../types/Tenant';
import User, { UserRole } from '../../../../types/User';
import AppAuthError from '../../../../exception/AppAuthError';
@@ -24,8 +25,6 @@ import Site from '../../../../types/Site';
import SiteArea from '../../../../types/SiteArea';
import SiteStorage from '../../../../storage/mongodb/SiteStorage';
import Tag from '../../../../types/Tag';
-import Tenant from '../../../../types/Tenant';
-import { TenantComponents } from '../../../../types/Tenant';
import UserStorage from '../../../../storage/mongodb/UserStorage';
import UserToken from '../../../../types/UserToken';
import Utils from '../../../../utils/Utils';
@@ -47,6 +46,8 @@ export default class AuthorizationService {
return authActions.canDelete;
case Action.UNASSIGN:
return authActions.canUnassign;
+ case Action.ASSIGN:
+ return authActions.canAssign;
case Action.UPDATE_BY_VISUAL_ID:
return authActions.canUpdateByVisualID;
case Action.ASSIGN_CHARGING_STATIONS_TO_SITE_AREA:
@@ -346,6 +347,8 @@ export default class AuthorizationService {
tenant, userToken, Entity.TAG, Action.UPDATE_BY_VISUAL_ID, authorizationFilter, { TagID: tag.id }, tag);
tag.canUnassign = await AuthorizationService.canPerformAuthorizationAction(
tenant, userToken, Entity.TAG, Action.UNASSIGN, authorizationFilter, { TagID: tag.id }, tag);
+ tag.canAssign = await AuthorizationService.canPerformAuthorizationAction(
+ tenant, userToken, Entity.TAG, Action.ASSIGN, authorizationFilter, { TagID: tag.id }, tag);
}
}

MODIFY
src/server/rest/v1/service/TagService.ts
src/server/rest/v1/service/TagService.ts
@@ -78,7 +78,16 @@ export default class TagService {
// Filter
const filteredRequest = TagValidator.getInstance().validateTagUnassign(req.body);
// Delete
- await TagService.unassignTags(req.tenant, action, req.user, [filteredRequest.visualID]);
+ const response = await TagService.unassignTags(req.tenant, action, req.user, [filteredRequest.visualID]);
+ if (response.inSuccess === 0) {
+ throw new AppError({
+ source: Constants.CENTRAL_SERVER,
+ action: ServerAction.TAG_UNASSIGN,
+ module: MODULE_NAME, method: 'handleUnassignTag',
+ errorCode: HTTPError.GENERAL_ERROR,
+ message: `Unable to unassign the Tag visualID '${filteredRequest.visualID}'`
+ });
+ }
// Return
res.json(Constants.REST_RESPONSE_SUCCESS);
next();
@@ -98,7 +107,16 @@ export default class TagService {
// Filter
const filteredRequest = TagValidator.getInstance().validateTagGetByID(req.query);
// Delete
- await TagService.deleteTags(req.tenant, action, req.user, [filteredRequest.ID]);
+ const response = await TagService.deleteTags(req.tenant, action, req.user, [filteredRequest.ID]);
+ if (response.inSuccess === 0) {
+ throw new AppError({
+ source: Constants.CENTRAL_SERVER,
+ action: ServerAction.TAG_DELETE,
+ module: MODULE_NAME, method: 'handleDeleteTag',
+ errorCode: HTTPError.GENERAL_ERROR,
+ message: `Unable to delete the Tag ID '${filteredRequest.ID}'`
+ });
+ }
// Return
res.json(Constants.REST_RESPONSE_SUCCESS);
next();
@@ -206,19 +224,10 @@ export default class TagService {
public static async handleAssignTag(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
const filteredRequest = TagValidator.getInstance().validateTagAssign(req.body);
- // Get dynamic auth
- const authorizationFilter = await AuthorizationService.checkAndGetTagAuthorizations(
- req.tenant, req.user, {}, Action.ASSIGN, filteredRequest);
- if (!authorizationFilter.authorized) {
- throw new AppAuthError({
- errorCode: HTTPAuthError.FORBIDDEN,
- user: req.user,
- action: Action.ASSIGN, entity: Entity.TAG,
- module: MODULE_NAME, method: 'handleAssignTag'
- });
- }
+ // Check and Get Tag
+ const tag = await UtilsService.checkAndGetTagByVisualIDAuthorization(req.tenant, req.user, filteredRequest.visualID, Action.ASSIGN, action,
+ filteredRequest, { withNbrTransactions: true, withUser: true });
// Check Tag with Visual ID
- const tag = await TagStorage.getTagByVisualID(req.tenant, filteredRequest.visualID, { withUser: true });
if (!tag) {
throw new AppError({
source: Constants.CENTRAL_SERVER,
@@ -324,7 +333,6 @@ export default class TagService {
}
// Update
tag.description = filteredRequest.description;
- tag.active = filteredRequest.active;
tag.default = filteredRequest.default;
tag.lastChangedBy = { id: req.user.id };
tag.lastChangedOn = new Date();

MODIFY
src/storage/mongodb/TagStorage.ts
src/storage/mongodb/TagStorage.ts
@@ -296,7 +296,7 @@ export default class TagStorage {
issuer: params.issuer,
active: true,
}, Constants.DB_PARAMS_SINGLE_RECORD, projectFields);
- return tagMDB.count === 1 ? tagMDB.result[0] : null;
+ return tagMDB.count > 0 ? tagMDB.result[0] : null;
}
public static async getDefaultUserTag(tenant: Tenant, userID: string,

MODIFY
src/types/Authorization.ts
src/types/Authorization.ts
@@ -187,6 +187,7 @@ export interface AuthorizationActions {
canUpdateByVisualID?: boolean;
canDelete?: boolean;
canUnassign?: boolean;
+ canAssign?: boolean;
}
export interface SiteAreaAuthorizationActions extends AuthorizationActions {
canAssignAssets?: boolean;
@@ -211,6 +212,7 @@ export enum DynamicAuthorizationFilterName {
ASSIGNED_SITES = 'AssignedSites',
OWN_USER = 'OwnUser',
LOCAL_ISSUER = 'LocalIssuer',
+ EXCLUDE_ACTION = 'ExcludeAction',
}
export enum DynamicAuthorizationAssertName {
@@ -224,6 +226,7 @@ export enum DynamicAuthorizationDataSourceName {
SITES_OWNER = 'SitesOwner',
ASSIGNED_SITES = 'AssignedSites',
OWN_USER = 'OwnUser',
+ EXCLUDE_ACTION = 'ExcludeAction',
}
export interface DynamicAuthorizationDataSourceData {}

Add ExcludeAction dynamic filter