From c9277ff8e9472ca539bdb51f974bb2db04bc5c60 Mon Sep 17 00:00:00 2001 From: sumeyyeKurtulus Date: Fri, 4 Oct 2024 17:02:25 +0300 Subject: [PATCH 01/12] update: add policy group for `ExtensionPropertyDto` --- .../src/lib/models/internal/object-extensions.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/npm/ng-packs/packages/components/extensible/src/lib/models/internal/object-extensions.ts b/npm/ng-packs/packages/components/extensible/src/lib/models/internal/object-extensions.ts index b89cd0216d..069b5ea71f 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/models/internal/object-extensions.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/models/internal/object-extensions.ts @@ -61,6 +61,19 @@ export interface ExtensionPropertyDto { configuration: Record; defaultValue: any; formText?: string; + policy?: PolicyGroup; +} + +interface PolicyDefinition { + features?: T; + permissionNames?: T; + requiresAll: boolean; +} + +export interface PolicyGroup { + globalFeatures: PolicyDefinition; + features: PolicyDefinition; + permissions: PolicyDefinition; } export interface ExtensionPropertyUiDto { From e282d6738edd393e19a164152a94f6a5ade83f79 Mon Sep 17 00:00:00 2001 From: sumeyyeKurtulus Date: Fri, 4 Oct 2024 17:03:02 +0300 Subject: [PATCH 02/12] update: conditional enhancement for `generateFormFromProps` function --- .../extensible/src/lib/utils/form-props.util.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/npm/ng-packs/packages/components/extensible/src/lib/utils/form-props.util.ts b/npm/ng-packs/packages/components/extensible/src/lib/utils/form-props.util.ts index 75b8487b31..1bd25966fc 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/utils/form-props.util.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/utils/form-props.util.ts @@ -24,7 +24,14 @@ export function generateFormFromProps(data: PropData) { props.forEach(({ value: prop }) => { const name = prop.name; const isExtraProperty = prop.isExtra || name in extraProperties; - let value = isExtraProperty ? extraProperties[name] : name in record ? record[name] : undefined; + + let value = undefined; + + if (isExtraProperty) { + value = extraProperties[name]; + } else if (name in record) { + value = record[name]; + } if (typeof value === 'undefined') value = prop.defaultValue; From 1672c49743b2fe4030b040847e2f452d33d7c813 Mon Sep 17 00:00:00 2001 From: sumeyyeKurtulus Date: Fri, 4 Oct 2024 17:04:33 +0300 Subject: [PATCH 03/12] =?UTF-8?q?update:=20add=20`checkPolicyProperties`?= =?UTF-8?q?=20function=20to=20handle=20policies=20=F0=9F=AA=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../extensible/src/lib/utils/props.util.ts | 161 ++++++++++++++++++ .../extensible/src/lib/utils/state.util.ts | 35 ++-- 2 files changed, 184 insertions(+), 12 deletions(-) diff --git a/npm/ng-packs/packages/components/extensible/src/lib/utils/props.util.ts b/npm/ng-packs/packages/components/extensible/src/lib/utils/props.util.ts index 7207858036..fc3fb7753a 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/utils/props.util.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/utils/props.util.ts @@ -22,6 +22,9 @@ import { PropList, PropsFactory, } from '../models/props'; +import { PolicyGroup } from '../models/internal/object-extensions'; +import { ObjectExtensions } from '../models/object-extensions'; +import { ConfigStateService, PermissionService } from '@abp/ng.core'; export function createExtraPropertyValueResolver( name: string, @@ -47,6 +50,59 @@ export function mergeWithDefaultProps>( ); }); } + +function isPolicyMet( + checkFunction: (item: string) => boolean, + requiresAll: boolean, + items?: string[], +): boolean { + if (!items?.length) { + return true; + } + return requiresAll ? items.every(checkFunction) : items.some(checkFunction); +} + +export function checkPolicyProperties( + properties: ObjectExtensions.EntityExtensionProperties, + configState: ConfigStateService, + permissionService?: PermissionService, +) { + //TODO this check will be removed after configuring every contribution in the row 🪄 + if (!permissionService) return; + + const checkPolicy = (policy: PolicyGroup): boolean => { + const { permissions, globalFeatures, features } = policy; + + const checks = [ + { + items: permissions?.permissionNames, + requiresAll: permissions?.requiresAll, + check: (item: string) => permissionService.getGrantedPolicy(item), + }, + { + items: globalFeatures?.features, + requiresAll: globalFeatures?.requiresAll, + check: (item: string) => configState.getGlobalFeatureIsEnabled(item), + }, + { + items: features?.features, + requiresAll: features?.requiresAll, + check: (item: string) => configState.getFeatureIsEnabled(item), + }, + ]; + + return checks.every(({ items, requiresAll, check }) => + isPolicyMet(check, requiresAll ?? false, items), + ); + }; + + Object.entries(properties).forEach(([name, property]) => { + if (property.policy && !checkPolicy(property.policy)) { + delete properties[name]; + } + }); +} + type InferredPropDefaults = F extends EntityPropsFactory ? EntityPropDefaults @@ -73,3 +129,108 @@ type InferredProps = : F extends EditFormPropsFactory ? FormProps : never; + +// export function checkPolicyProperties( +// properties: ObjectExtensions.EntityExtensionProperties, +// configState: ConfigStateService, +// permissionService?: PermissionService, +// ) { +// Object.keys(properties).forEach((name: string) => { +// const property = properties[name]; + +// if (!property.policy) { +// return; +// } + +// let isPolicyConstraintMet = false; + +// const { permissions, features, globalFeatures } = property.policy; + +// if (!permissionService) { +// return; +// } + +// if (!permissions.permissionNames) { +// return; +// } + +// const hasPermission = (permission: string): boolean => +// permissionService.getGrantedPolicy(permission); + +// isPolicyConstraintMet = permissions.requiresAll +// ? permissions.permissionNames.every(hasPermission) +// : permissions.permissionNames.some(hasPermission); + +// if (!isPolicyConstraintMet) { +// delete properties[name]; +// } + +// if (!globalFeatures.features) { +// return; +// } + +// const hasGlobalFeature = (globalFeature: string): boolean => +// configState.getGlobalFeatureIsEnabled(globalFeature); + +// isPolicyConstraintMet = globalFeatures.requiresAll +// ? globalFeatures.features.every(hasGlobalFeature) +// : globalFeatures.features.some(hasGlobalFeature); + +// if (!isPolicyConstraintMet) { +// delete properties[name]; +// } + +// const hasFeature = (feature: string): boolean => configState.getFeatureIsEnabled(feature); + +// isPolicyConstraintMet = features.requiresAll +// ? features.features.every(hasFeature) +// : features.features.some(hasFeature); + +// if (!isPolicyConstraintMet) { +// delete properties[name]; +// } +// }); +// } + +// export function checkPolicyProperties( +// properties: ObjectExtensions.EntityExtensionProperties, +// configState: ConfigStateService, +// permissionService?: PermissionService, +// ) { +// if (!permissionService) return; + +// const isConstraintMet = ( +// items: string[] | undefined, +// requiresAll: boolean | undefined, +// checkFunction: (item: string) => boolean, +// ): boolean => +// !items || +// items.length === 0 || +// (requiresAll ? items.every(checkFunction) : items.some(checkFunction)); + +// const policyCheckers = [ +// { +// getItems: (policy: any) => policy.permissions?.permissionNames, +// check: (permission: string) => permissionService.getGrantedPolicy(permission), +// }, +// { +// getItems: (policy: any) => policy.globalFeatures?.features, +// check: (feature: string) => configState.getGlobalFeatureIsEnabled(feature), +// }, +// { +// getItems: (policy: any) => policy.features?.features, +// check: (feature: string) => configState.getFeatureIsEnabled(feature), +// }, +// ]; + +// Object.keys(properties).forEach((name: string) => { +// const { policy } = properties[name]; +// if (!policy) return; + +// const shouldDelete = policyCheckers.some( +// ({ getItems, check }) => !isConstraintMet(getItems(policy), policy.features.requiresAll, check), +// ); + +// if (shouldDelete) delete properties[name]; +// }); +// } diff --git a/npm/ng-packs/packages/components/extensible/src/lib/utils/state.util.ts b/npm/ng-packs/packages/components/extensible/src/lib/utils/state.util.ts index 2d545c1162..483dcb8a59 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/utils/state.util.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/utils/state.util.ts @@ -5,6 +5,7 @@ import { ExtensionEnumDto, ExtensionPropertyUiLookupDto, ObjectExtensionsDto, + PermissionService, } from '@abp/ng.core'; import { Observable, pipe, zip } from 'rxjs'; import { filter, map, switchMap, take } from 'rxjs/operators'; @@ -15,7 +16,7 @@ import { ObjectExtensions } from '../models/object-extensions'; import { PropCallback } from '../models/props'; import { createEnum, createEnumOptions, createEnumValueResolver } from './enum.util'; import { createDisplayNameLocalizationPipeKeyGenerator } from './localization.util'; -import { createExtraPropertyValueResolver } from './props.util'; +import { checkPolicyProperties, createExtraPropertyValueResolver } from './props.util'; import { createTypeaheadDisplayNameGenerator, createTypeaheadOptions, @@ -39,15 +40,18 @@ function selectEnums( ): Observable> { return selectObjectExtensions(configState).pipe( map((extensions: ObjectExtensionsDto) => - Object.keys(extensions.enums).reduce((acc, key) => { - const { fields, localizationResource } = extensions.enums[key]; - acc[key] = { - fields, - localizationResource, - transformed: createEnum(fields), - }; - return acc; - }, {} as Record), + Object.keys(extensions.enums).reduce( + (acc, key) => { + const { fields, localizationResource } = extensions.enums[key]; + acc[key] = { + fields, + localizationResource, + transformed: createEnum(fields), + }; + return acc; + }, + {} as Record, + ), ), ); } @@ -72,6 +76,7 @@ export function getObjectExtensionEntitiesFromStore( export function mapEntitiesToContributors( configState: ConfigStateService, resource: string, + permissionService?: PermissionService, ) { return pipe( switchMap((entities: any) => @@ -86,10 +91,16 @@ export function mapEntitiesToContributors( acc.editForm[key] = []; const entity: ObjectExtensions.EntityExtensionDto = entities[key]; - if (!entity) return acc; + if (!entity) { + return acc; + } const properties = entity.properties; - if (!properties) return acc; + if (!properties) { + return acc; + } + + checkPolicyProperties(properties, configState, permissionService); const mapPropertiesToContributors = createPropertiesToContributorsMapper( generateDisplayName, From a3f461ee3858965c36451840e68f3307d19cbf40 Mon Sep 17 00:00:00 2001 From: sumeyyeKurtulus Date: Fri, 4 Oct 2024 17:05:31 +0300 Subject: [PATCH 04/12] update: add feature enable check functions for the config state service --- .../src/lib/services/config-state.service.ts | 41 ++++++++++++++----- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/services/config-state.service.ts b/npm/ng-packs/packages/core/src/lib/services/config-state.service.ts index 97fcdbc40b..5a5f244d43 100644 --- a/npm/ng-packs/packages/core/src/lib/services/config-state.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/config-state.service.ts @@ -5,6 +5,7 @@ import { AbpApplicationConfigurationService } from '../proxy/volo/abp/asp-net-co import { AbpApplicationLocalizationService } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/abp-application-localization.service'; import { ApplicationConfigurationDto, + ApplicationFeatureConfigurationDto, ApplicationGlobalFeatureConfigurationDto, } from '../proxy/volo/abp/asp-net-core/mvc/application-configurations/models'; import { INCUDE_LOCALIZATION_RESOURCES_TOKEN } from '../tokens/include-localization-resources.token'; @@ -71,10 +72,10 @@ export class ConfigStateService { } refreshLocalization(lang: string): Observable { - if(this.includeLocalizationResources){ + if (this.includeLocalizationResources) { return this.refreshAppState().pipe(map(() => null)); } - + return this.getlocalizationResource(lang) .pipe( tap(result => @@ -145,7 +146,7 @@ export class ConfigStateService { return keys.reduce((acc, key) => ({ ...acc, [key]: features.values[key] }), {}); } - getFeatures$(keys: string[]): Observable<{ [key: string]: string; } | undefined> { + getFeatures$(keys: string[]): Observable<{ [key: string]: string } | undefined> { return this.store.sliceState(({ features }) => { if (!features?.values) return; @@ -153,6 +154,18 @@ export class ConfigStateService { }); } + private isFeatureEnabled(key: string, features: ApplicationFeatureConfigurationDto) { + return features.values[key] === 'true'; + } + + getFeatureIsEnabled(key: string) { + return this.isFeatureEnabled(key, this.store.state.features); + } + + getFeatureIsEnabled$(key: string) { + return this.store.sliceState(state => this.isFeatureEnabled(key, state.features)); + } + getSetting(key: string) { return this.store.state.setting?.values?.[key]; } @@ -168,10 +181,13 @@ export class ConfigStateService { const keysFound = Object.keys(settings).filter(key => key.indexOf(keyword) > -1); - return keysFound.reduce((acc, key) => { - acc[key] = settings[key]; - return acc; - }, {} as Record); + return keysFound.reduce( + (acc, key) => { + acc[key] = settings[key]; + return acc; + }, + {} as Record, + ); } getSettings$(keyword?: string) { @@ -183,10 +199,13 @@ export class ConfigStateService { const keysFound = Object.keys(settings).filter(key => key.indexOf(keyword) > -1); - return keysFound.reduce((acc, key) => { - acc[key] = settings[key]; - return acc; - }, {} as Record); + return keysFound.reduce( + (acc, key) => { + acc[key] = settings[key]; + return acc; + }, + {} as Record, + ); }), ); } From c3cfb5d262b6e606b952b4840e294abe41d0cbca Mon Sep 17 00:00:00 2001 From: sumeyyeKurtulus Date: Fri, 4 Oct 2024 17:06:40 +0300 Subject: [PATCH 05/12] add: `permissionService` for making the permission checks on identity contributions --- .../identity/src/lib/resolvers/extensions.resolver.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/npm/ng-packs/packages/identity/src/lib/resolvers/extensions.resolver.ts b/npm/ng-packs/packages/identity/src/lib/resolvers/extensions.resolver.ts index 45856046d8..671f9db81f 100644 --- a/npm/ng-packs/packages/identity/src/lib/resolvers/extensions.resolver.ts +++ b/npm/ng-packs/packages/identity/src/lib/resolvers/extensions.resolver.ts @@ -1,6 +1,6 @@ import { inject } from '@angular/core'; import { map, tap } from 'rxjs'; -import { ConfigStateService } from '@abp/ng.core'; +import { ConfigStateService, PermissionService } from '@abp/ng.core'; import { ExtensionsService, getObjectExtensionEntitiesFromStore, @@ -25,6 +25,7 @@ import { ResolveFn } from '@angular/router'; export const identityExtensionsResolver: ResolveFn = () => { const configState = inject(ConfigStateService); + const permissionService = inject(PermissionService); const extensions = inject(ExtensionsService); const config = { optional: true }; @@ -40,7 +41,7 @@ export const identityExtensionsResolver: ResolveFn = () => { [eIdentityComponents.Roles]: entities.Role, [eIdentityComponents.Users]: entities.User, })), - mapEntitiesToContributors(configState, 'AbpIdentity'), + mapEntitiesToContributors(configState, 'AbpIdentity', permissionService), tap(objectExtensionContributors => { mergeWithDefaultActions( extensions.entityActions, From 47fa3cb9b873e8ff0a6379457c02d9187780b22a Mon Sep 17 00:00:00 2001 From: sumeyyeKurtulus Date: Mon, 7 Oct 2024 11:32:10 +0300 Subject: [PATCH 06/12] update: add permission service prop for `mapEntitiesToContributors` function --- .../src/lib/guards/extensions.guard.ts | 13 +- .../src/lib/resolvers/extensions.resolver.ts | 11 +- .../extensible/src/lib/utils/props.util.ts | 112 +----------------- .../extensible/src/lib/utils/state.util.ts | 2 +- .../src/lib/guards/extensions.guard.ts | 14 +-- .../src/lib/resolvers/extensions.resolver.ts | 12 +- .../src/lib/guards/extensions.guard.ts | 16 ++- .../src/lib/resolvers/extensions.resolver.ts | 11 +- 8 files changed, 41 insertions(+), 150 deletions(-) diff --git a/npm/ng-packs/packages/account/src/lib/guards/extensions.guard.ts b/npm/ng-packs/packages/account/src/lib/guards/extensions.guard.ts index ebeae81a67..d395c49ccc 100644 --- a/npm/ng-packs/packages/account/src/lib/guards/extensions.guard.ts +++ b/npm/ng-packs/packages/account/src/lib/guards/extensions.guard.ts @@ -1,15 +1,13 @@ -import { Injectable, inject } from '@angular/core'; - -import { Observable } from 'rxjs'; -import { tap, map } from 'rxjs/operators'; - -import { ConfigStateService, IAbpGuard } from '@abp/ng.core'; import { ExtensionsService, getObjectExtensionEntitiesFromStore, mapEntitiesToContributors, mergeWithDefaultProps, } from '@abp/ng.components/extensible'; +import { ConfigStateService, IAbpGuard, PermissionService } from '@abp/ng.core'; +import { Injectable, inject } from '@angular/core'; +import { Observable } from 'rxjs'; +import { tap, map } from 'rxjs/operators'; import { ACCOUNT_EDIT_FORM_PROP_CONTRIBUTORS, @@ -23,6 +21,7 @@ import { eAccountComponents } from '../enums/components'; @Injectable() export class AccountExtensionsGuard implements IAbpGuard { protected readonly configState = inject(ConfigStateService); + protected readonly permmission = inject(PermissionService); protected readonly extensions = inject(ExtensionsService); canActivate(): Observable { @@ -34,7 +33,7 @@ export class AccountExtensionsGuard implements IAbpGuard { map(entities => ({ [eAccountComponents.PersonalSettings]: entities.User, })), - mapEntitiesToContributors(this.configState, 'AbpIdentity'), + mapEntitiesToContributors(this.configState, this.permmission, 'AbpIdentity'), tap(objectExtensionContributors => { mergeWithDefaultProps( this.extensions.editFormProps, diff --git a/npm/ng-packs/packages/account/src/lib/resolvers/extensions.resolver.ts b/npm/ng-packs/packages/account/src/lib/resolvers/extensions.resolver.ts index b3aec858b8..2ea2ce6e97 100644 --- a/npm/ng-packs/packages/account/src/lib/resolvers/extensions.resolver.ts +++ b/npm/ng-packs/packages/account/src/lib/resolvers/extensions.resolver.ts @@ -1,18 +1,19 @@ -import { inject } from '@angular/core'; -import { ConfigStateService } from '@abp/ng.core'; -import { map, tap } from 'rxjs'; import { ExtensionsService, getObjectExtensionEntitiesFromStore, mapEntitiesToContributors, mergeWithDefaultProps, } from '@abp/ng.components/extensible'; +import { ConfigStateService, PermissionService } from '@abp/ng.core'; +import { inject } from '@angular/core'; +import { ResolveFn } from '@angular/router'; +import { map, tap } from 'rxjs'; import { eAccountComponents } from '../enums'; import { ACCOUNT_EDIT_FORM_PROP_CONTRIBUTORS, DEFAULT_ACCOUNT_FORM_PROPS } from '../tokens'; -import { ResolveFn } from '@angular/router'; export const accountExtensionsResolver: ResolveFn = () => { const configState = inject(ConfigStateService); + const permission = inject(PermissionService); const extensions = inject(ExtensionsService); const config = { optional: true }; @@ -23,7 +24,7 @@ export const accountExtensionsResolver: ResolveFn = () => { map(entities => ({ [eAccountComponents.PersonalSettings]: entities.User, })), - mapEntitiesToContributors(configState, 'AbpIdentity'), + mapEntitiesToContributors(configState, permission, 'AbpIdentity'), tap(objectExtensionContributors => { mergeWithDefaultProps( extensions.editFormProps, diff --git a/npm/ng-packs/packages/components/extensible/src/lib/utils/props.util.ts b/npm/ng-packs/packages/components/extensible/src/lib/utils/props.util.ts index fc3fb7753a..c3f73afc0c 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/utils/props.util.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/utils/props.util.ts @@ -1,3 +1,4 @@ +import { ConfigStateService, PermissionService } from '@abp/ng.core'; import { Observable, of } from 'rxjs'; import { EXTRA_PROPERTIES_KEY } from '../constants/extra-properties'; import { @@ -24,7 +25,6 @@ import { } from '../models/props'; import { PolicyGroup } from '../models/internal/object-extensions'; import { ObjectExtensions } from '../models/object-extensions'; -import { ConfigStateService, PermissionService } from '@abp/ng.core'; export function createExtraPropertyValueResolver( name: string, @@ -65,11 +65,8 @@ function isPolicyMet( export function checkPolicyProperties( properties: ObjectExtensions.EntityExtensionProperties, configState: ConfigStateService, - permissionService?: PermissionService, + permissionService: PermissionService, ) { - //TODO this check will be removed after configuring every contribution in the row 🪄 - if (!permissionService) return; - const checkPolicy = (policy: PolicyGroup): boolean => { const { permissions, globalFeatures, features } = policy; @@ -129,108 +126,3 @@ type InferredProps = : F extends EditFormPropsFactory ? FormProps : never; - -// export function checkPolicyProperties( -// properties: ObjectExtensions.EntityExtensionProperties, -// configState: ConfigStateService, -// permissionService?: PermissionService, -// ) { -// Object.keys(properties).forEach((name: string) => { -// const property = properties[name]; - -// if (!property.policy) { -// return; -// } - -// let isPolicyConstraintMet = false; - -// const { permissions, features, globalFeatures } = property.policy; - -// if (!permissionService) { -// return; -// } - -// if (!permissions.permissionNames) { -// return; -// } - -// const hasPermission = (permission: string): boolean => -// permissionService.getGrantedPolicy(permission); - -// isPolicyConstraintMet = permissions.requiresAll -// ? permissions.permissionNames.every(hasPermission) -// : permissions.permissionNames.some(hasPermission); - -// if (!isPolicyConstraintMet) { -// delete properties[name]; -// } - -// if (!globalFeatures.features) { -// return; -// } - -// const hasGlobalFeature = (globalFeature: string): boolean => -// configState.getGlobalFeatureIsEnabled(globalFeature); - -// isPolicyConstraintMet = globalFeatures.requiresAll -// ? globalFeatures.features.every(hasGlobalFeature) -// : globalFeatures.features.some(hasGlobalFeature); - -// if (!isPolicyConstraintMet) { -// delete properties[name]; -// } - -// const hasFeature = (feature: string): boolean => configState.getFeatureIsEnabled(feature); - -// isPolicyConstraintMet = features.requiresAll -// ? features.features.every(hasFeature) -// : features.features.some(hasFeature); - -// if (!isPolicyConstraintMet) { -// delete properties[name]; -// } -// }); -// } - -// export function checkPolicyProperties( -// properties: ObjectExtensions.EntityExtensionProperties, -// configState: ConfigStateService, -// permissionService?: PermissionService, -// ) { -// if (!permissionService) return; - -// const isConstraintMet = ( -// items: string[] | undefined, -// requiresAll: boolean | undefined, -// checkFunction: (item: string) => boolean, -// ): boolean => -// !items || -// items.length === 0 || -// (requiresAll ? items.every(checkFunction) : items.some(checkFunction)); - -// const policyCheckers = [ -// { -// getItems: (policy: any) => policy.permissions?.permissionNames, -// check: (permission: string) => permissionService.getGrantedPolicy(permission), -// }, -// { -// getItems: (policy: any) => policy.globalFeatures?.features, -// check: (feature: string) => configState.getGlobalFeatureIsEnabled(feature), -// }, -// { -// getItems: (policy: any) => policy.features?.features, -// check: (feature: string) => configState.getFeatureIsEnabled(feature), -// }, -// ]; - -// Object.keys(properties).forEach((name: string) => { -// const { policy } = properties[name]; -// if (!policy) return; - -// const shouldDelete = policyCheckers.some( -// ({ getItems, check }) => !isConstraintMet(getItems(policy), policy.features.requiresAll, check), -// ); - -// if (shouldDelete) delete properties[name]; -// }); -// } diff --git a/npm/ng-packs/packages/components/extensible/src/lib/utils/state.util.ts b/npm/ng-packs/packages/components/extensible/src/lib/utils/state.util.ts index 483dcb8a59..f6bbdf5b2d 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/utils/state.util.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/utils/state.util.ts @@ -75,8 +75,8 @@ export function getObjectExtensionEntitiesFromStore( export function mapEntitiesToContributors( configState: ConfigStateService, + permissionService: PermissionService, resource: string, - permissionService?: PermissionService, ) { return pipe( switchMap((entities: any) => diff --git a/npm/ng-packs/packages/identity/src/lib/guards/extensions.guard.ts b/npm/ng-packs/packages/identity/src/lib/guards/extensions.guard.ts index c78e03b6af..5d78bba9fb 100644 --- a/npm/ng-packs/packages/identity/src/lib/guards/extensions.guard.ts +++ b/npm/ng-packs/packages/identity/src/lib/guards/extensions.guard.ts @@ -1,9 +1,3 @@ -import { Injectable, inject } from '@angular/core'; - -import { Observable } from 'rxjs'; -import { map, tap } from 'rxjs/operators'; - -import { ConfigStateService, IAbpGuard } from '@abp/ng.core'; import { ExtensionsService, getObjectExtensionEntitiesFromStore, @@ -11,6 +5,11 @@ import { mergeWithDefaultActions, mergeWithDefaultProps, } from '@abp/ng.components/extensible'; +import { ConfigStateService, IAbpGuard, PermissionService } from '@abp/ng.core'; +import { Injectable, inject } from '@angular/core'; + +import { Observable } from 'rxjs'; +import { map, tap } from 'rxjs/operators'; import { eIdentityComponents } from '../enums/components'; import { @@ -32,6 +31,7 @@ import { @Injectable() export class IdentityExtensionsGuard implements IAbpGuard { protected readonly configState = inject(ConfigStateService); + protected readonly permission = inject(PermissionService); protected readonly extensions = inject(ExtensionsService); canActivate(): Observable { @@ -48,7 +48,7 @@ export class IdentityExtensionsGuard implements IAbpGuard { [eIdentityComponents.Roles]: entities.Role, [eIdentityComponents.Users]: entities.User, })), - mapEntitiesToContributors(this.configState, 'AbpIdentity'), + mapEntitiesToContributors(this.configState, this.permission, 'AbpIdentity'), tap(objectExtensionContributors => { mergeWithDefaultActions( this.extensions.entityActions, diff --git a/npm/ng-packs/packages/identity/src/lib/resolvers/extensions.resolver.ts b/npm/ng-packs/packages/identity/src/lib/resolvers/extensions.resolver.ts index 671f9db81f..812a63d3f7 100644 --- a/npm/ng-packs/packages/identity/src/lib/resolvers/extensions.resolver.ts +++ b/npm/ng-packs/packages/identity/src/lib/resolvers/extensions.resolver.ts @@ -1,6 +1,3 @@ -import { inject } from '@angular/core'; -import { map, tap } from 'rxjs'; -import { ConfigStateService, PermissionService } from '@abp/ng.core'; import { ExtensionsService, getObjectExtensionEntitiesFromStore, @@ -8,6 +5,10 @@ import { mergeWithDefaultActions, mergeWithDefaultProps, } from '@abp/ng.components/extensible'; +import { ConfigStateService, PermissionService } from '@abp/ng.core'; +import { inject } from '@angular/core'; +import { ResolveFn } from '@angular/router'; +import { map, tap } from 'rxjs'; import { eIdentityComponents } from '../enums'; import { IDENTITY_ENTITY_ACTION_CONTRIBUTORS, @@ -21,11 +22,10 @@ import { DEFAULT_IDENTITY_CREATE_FORM_PROPS, DEFAULT_IDENTITY_EDIT_FORM_PROPS, } from '../tokens'; -import { ResolveFn } from '@angular/router'; export const identityExtensionsResolver: ResolveFn = () => { const configState = inject(ConfigStateService); - const permissionService = inject(PermissionService); + const permission = inject(PermissionService); const extensions = inject(ExtensionsService); const config = { optional: true }; @@ -41,7 +41,7 @@ export const identityExtensionsResolver: ResolveFn = () => { [eIdentityComponents.Roles]: entities.Role, [eIdentityComponents.Users]: entities.User, })), - mapEntitiesToContributors(configState, 'AbpIdentity', permissionService), + mapEntitiesToContributors(configState, permission, 'AbpIdentity'), tap(objectExtensionContributors => { mergeWithDefaultActions( extensions.entityActions, diff --git a/npm/ng-packs/packages/tenant-management/src/lib/guards/extensions.guard.ts b/npm/ng-packs/packages/tenant-management/src/lib/guards/extensions.guard.ts index d37d735453..0b140f8de8 100644 --- a/npm/ng-packs/packages/tenant-management/src/lib/guards/extensions.guard.ts +++ b/npm/ng-packs/packages/tenant-management/src/lib/guards/extensions.guard.ts @@ -1,9 +1,3 @@ -import { Injectable, inject } from '@angular/core'; - -import { Observable } from 'rxjs'; -import { map, tap } from 'rxjs/operators'; - -import { ConfigStateService, IAbpGuard } from '@abp/ng.core'; import { ExtensionsService, getObjectExtensionEntitiesFromStore, @@ -11,6 +5,11 @@ import { mergeWithDefaultActions, mergeWithDefaultProps, } from '@abp/ng.components/extensible'; +import { ConfigStateService, IAbpGuard, PermissionService } from '@abp/ng.core'; +import { Injectable, inject } from '@angular/core'; + +import { Observable } from 'rxjs'; +import { map, tap } from 'rxjs/operators'; import { eTenantManagementComponents } from '../enums/components'; import { @@ -32,6 +31,7 @@ import { @Injectable() export class TenantManagementExtensionsGuard implements IAbpGuard { protected readonly configState = inject(ConfigStateService); + protected readonly permission = inject(PermissionService); protected readonly extensions = inject(ExtensionsService); canActivate(): Observable { @@ -49,7 +49,7 @@ export class TenantManagementExtensionsGuard implements IAbpGuard { map(entities => ({ [eTenantManagementComponents.Tenants]: entities.Tenant, })), - mapEntitiesToContributors(this.configState, 'TenantManagement'), + mapEntitiesToContributors(this.configState, this.permission, 'TenantManagement'), tap(objectExtensionContributors => { mergeWithDefaultActions( this.extensions.entityActions, @@ -84,5 +84,3 @@ export class TenantManagementExtensionsGuard implements IAbpGuard { ); } } - - diff --git a/npm/ng-packs/packages/tenant-management/src/lib/resolvers/extensions.resolver.ts b/npm/ng-packs/packages/tenant-management/src/lib/resolvers/extensions.resolver.ts index d3c224aba2..98e3f34aa6 100644 --- a/npm/ng-packs/packages/tenant-management/src/lib/resolvers/extensions.resolver.ts +++ b/npm/ng-packs/packages/tenant-management/src/lib/resolvers/extensions.resolver.ts @@ -1,6 +1,3 @@ -import { inject } from '@angular/core'; -import { map, tap } from 'rxjs'; -import { ConfigStateService } from '@abp/ng.core'; import { ExtensionsService, getObjectExtensionEntitiesFromStore, @@ -8,6 +5,10 @@ import { mergeWithDefaultActions, mergeWithDefaultProps, } from '@abp/ng.components/extensible'; +import { ConfigStateService, PermissionService } from '@abp/ng.core'; +import { inject } from '@angular/core'; +import { ResolveFn } from '@angular/router'; +import { map, tap } from 'rxjs'; import { eTenantManagementComponents } from '../enums'; import { TENANT_MANAGEMENT_ENTITY_ACTION_CONTRIBUTORS, @@ -21,10 +22,10 @@ import { DEFAULT_TENANT_MANAGEMENT_CREATE_FORM_PROPS, DEFAULT_TENANT_MANAGEMENT_EDIT_FORM_PROPS, } from '../tokens'; -import { ResolveFn } from '@angular/router'; export const tenantManagementExtensionsResolver: ResolveFn = () => { const configState = inject(ConfigStateService); + const permission = inject(PermissionService); const extensions = inject(ExtensionsService); const config = { optional: true }; @@ -40,7 +41,7 @@ export const tenantManagementExtensionsResolver: ResolveFn = () => { map(entities => ({ [eTenantManagementComponents.Tenants]: entities.Tenant, })), - mapEntitiesToContributors(configState, 'TenantManagement'), + mapEntitiesToContributors(configState, permission, 'TenantManagement'), tap(objectExtensionContributors => { mergeWithDefaultActions( extensions.entityActions, From e2360a89ce5b8eb81f2a0dfe068176fbd4c14657 Mon Sep 17 00:00:00 2001 From: sumeyyeKurtulus Date: Mon, 7 Oct 2024 11:33:46 +0300 Subject: [PATCH 07/12] update: tests for the state util --- .../extensible/src/tests/state.util.spec.ts | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/npm/ng-packs/packages/components/extensible/src/tests/state.util.spec.ts b/npm/ng-packs/packages/components/extensible/src/tests/state.util.spec.ts index 58235e1590..006ea1101e 100644 --- a/npm/ng-packs/packages/components/extensible/src/tests/state.util.spec.ts +++ b/npm/ng-packs/packages/components/extensible/src/tests/state.util.spec.ts @@ -1,30 +1,30 @@ -import {ConfigStateService} from '@abp/ng.core'; -import {firstValueFrom, of} from 'rxjs'; -import {take} from 'rxjs/operators'; -import {ePropType} from '../lib/enums/props.enum'; -import {EntityPropList} from '../lib/models/entity-props'; -import {FormPropList} from '../lib/models/form-props'; -import {ObjectExtensions} from '../lib/models/object-extensions'; +import { ConfigStateService, PermissionService } from '@abp/ng.core'; +import { firstValueFrom, lastValueFrom, of } from 'rxjs'; +import { take } from 'rxjs/operators'; +import { ePropType } from '../lib/enums/props.enum'; +import { EntityPropList } from '../lib/models/entity-props'; +import { FormPropList } from '../lib/models/form-props'; +import { ObjectExtensions } from '../lib/models/object-extensions'; import { getObjectExtensionEntitiesFromStore, mapEntitiesToContributors, } from '../lib/utils/state.util'; -const fakeAppConfigService = {get: () => of(createMockState())} as any; -const fakeLocalizationService = {get: () => of(createMockState())} as any; +const fakeAppConfigService = { get: () => of(createMockState()) } as any; +const fakeLocalizationService = { get: () => of(createMockState()) } as any; const configState = new ConfigStateService(fakeAppConfigService, fakeLocalizationService, false); configState.refreshAppState(); +const permissionService = new PermissionService(configState); describe('State Utils', () => { describe('#getObjectExtensionEntitiesFromStore', () => { it('should return observable entities of an existing module', async () => { - const objectExtensionEntitiesFromStore$ = getObjectExtensionEntitiesFromStore( configState, 'Identity', - ) + ); - const entities = await firstValueFrom(objectExtensionEntitiesFromStore$) + const entities = await firstValueFrom(objectExtensionEntitiesFromStore$); expect('Role' in entities).toBe(true); }); @@ -48,9 +48,12 @@ describe('State Utils', () => { describe('#mapEntitiesToContributors', () => { it('should return contributors from given entities', async () => { - const contributors = await of(createMockEntities()) - .pipe(mapEntitiesToContributors(configState, 'AbpIdentity'), take(1)) - .toPromise(); + const contributors = await lastValueFrom( + of(createMockEntities()).pipe( + mapEntitiesToContributors(configState, permissionService, 'AbpIdentity'), + take(1), + ), + ); const propList = new EntityPropList(); contributors.prop.Role.forEach(callback => callback(propList)); @@ -118,7 +121,7 @@ function createMockState() { }, defaultResourceName: 'Default', currentCulture: { - cultureName: 'en' + cultureName: 'en', }, languages: [], }, From bf877e400dd198b0de6997a8be1930a1f73be33b Mon Sep 17 00:00:00 2001 From: Masum ULU <49063256+masum-ulu@users.noreply.github.com> Date: Tue, 8 Oct 2024 08:47:47 +0300 Subject: [PATCH 08/12] Update extensions.guard.ts --- .../packages/account/src/lib/guards/extensions.guard.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/npm/ng-packs/packages/account/src/lib/guards/extensions.guard.ts b/npm/ng-packs/packages/account/src/lib/guards/extensions.guard.ts index d395c49ccc..a67db6c7b9 100644 --- a/npm/ng-packs/packages/account/src/lib/guards/extensions.guard.ts +++ b/npm/ng-packs/packages/account/src/lib/guards/extensions.guard.ts @@ -1,13 +1,13 @@ +import { Injectable, inject } from '@angular/core'; +import { Observable } from 'rxjs'; +import { tap, map } from 'rxjs/operators'; +import { ConfigStateService, IAbpGuard, PermissionService } from '@abp/ng.core'; import { ExtensionsService, getObjectExtensionEntitiesFromStore, mapEntitiesToContributors, mergeWithDefaultProps, } from '@abp/ng.components/extensible'; -import { ConfigStateService, IAbpGuard, PermissionService } from '@abp/ng.core'; -import { Injectable, inject } from '@angular/core'; -import { Observable } from 'rxjs'; -import { tap, map } from 'rxjs/operators'; import { ACCOUNT_EDIT_FORM_PROP_CONTRIBUTORS, From 2d73baebd68064c7e31bff248757684341985937 Mon Sep 17 00:00:00 2001 From: Masum ULU <49063256+masum-ulu@users.noreply.github.com> Date: Tue, 8 Oct 2024 08:48:22 +0300 Subject: [PATCH 09/12] Update extensions.resolver.ts --- .../account/src/lib/resolvers/extensions.resolver.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/npm/ng-packs/packages/account/src/lib/resolvers/extensions.resolver.ts b/npm/ng-packs/packages/account/src/lib/resolvers/extensions.resolver.ts index 2ea2ce6e97..e744e98442 100644 --- a/npm/ng-packs/packages/account/src/lib/resolvers/extensions.resolver.ts +++ b/npm/ng-packs/packages/account/src/lib/resolvers/extensions.resolver.ts @@ -1,13 +1,13 @@ +import { inject } from '@angular/core'; +import { ResolveFn } from '@angular/router'; +import { map, tap } from 'rxjs'; +import { ConfigStateService, PermissionService } from '@abp/ng.core'; import { ExtensionsService, getObjectExtensionEntitiesFromStore, mapEntitiesToContributors, mergeWithDefaultProps, } from '@abp/ng.components/extensible'; -import { ConfigStateService, PermissionService } from '@abp/ng.core'; -import { inject } from '@angular/core'; -import { ResolveFn } from '@angular/router'; -import { map, tap } from 'rxjs'; import { eAccountComponents } from '../enums'; import { ACCOUNT_EDIT_FORM_PROP_CONTRIBUTORS, DEFAULT_ACCOUNT_FORM_PROPS } from '../tokens'; From d22120e1d164579a36b23c5a477f8c2cfa0955c5 Mon Sep 17 00:00:00 2001 From: Masum ULU <49063256+masum-ulu@users.noreply.github.com> Date: Tue, 8 Oct 2024 11:15:16 +0300 Subject: [PATCH 10/12] Update object-extensions.ts --- .../lib/models/internal/object-extensions.ts | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/npm/ng-packs/packages/components/extensible/src/lib/models/internal/object-extensions.ts b/npm/ng-packs/packages/components/extensible/src/lib/models/internal/object-extensions.ts index 069b5ea71f..2fe8593f8d 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/models/internal/object-extensions.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/models/internal/object-extensions.ts @@ -61,19 +61,23 @@ export interface ExtensionPropertyDto { configuration: Record; defaultValue: any; formText?: string; - policy?: PolicyGroup; + policy?: Policy; } -interface PolicyDefinition { - features?: T; - permissionNames?: T; +export interface BaseDefinition { requiresAll: boolean; } +export interface FeatureDefinition extends BaseDefinition { + features?: string[]; +} +export interface PermissionDefinition extends BaseDefinition { + permissionNames?: string[]; +} -export interface PolicyGroup { - globalFeatures: PolicyDefinition; - features: PolicyDefinition; - permissions: PolicyDefinition; +export interface Policy { + globalFeatures: FeatureDefinition; + features: FeatureDefinition; + permissions: PermissionDefinition; } export interface ExtensionPropertyUiDto { From 876117150b5de37f67eb97019d6e4a0b1b0f4877 Mon Sep 17 00:00:00 2001 From: Masum ULU <49063256+masum-ulu@users.noreply.github.com> Date: Tue, 8 Oct 2024 11:15:52 +0300 Subject: [PATCH 11/12] Update props.util.ts --- .../components/extensible/src/lib/utils/props.util.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/npm/ng-packs/packages/components/extensible/src/lib/utils/props.util.ts b/npm/ng-packs/packages/components/extensible/src/lib/utils/props.util.ts index c3f73afc0c..fa6f60eec1 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/utils/props.util.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/utils/props.util.ts @@ -23,7 +23,7 @@ import { PropList, PropsFactory, } from '../models/props'; -import { PolicyGroup } from '../models/internal/object-extensions'; +import { Policy } from '../models/internal/object-extensions'; import { ObjectExtensions } from '../models/object-extensions'; export function createExtraPropertyValueResolver( @@ -67,7 +67,7 @@ export function checkPolicyProperties( configState: ConfigStateService, permissionService: PermissionService, ) { - const checkPolicy = (policy: PolicyGroup): boolean => { + const checkPolicy = (policy: Policy): boolean => { const { permissions, globalFeatures, features } = policy; const checks = [ From 96c3e1be3c203ef935f6d613c284b3de8b5b4dd2 Mon Sep 17 00:00:00 2001 From: masum-ulu Date: Tue, 8 Oct 2024 11:39:32 +0300 Subject: [PATCH 12/12] policy function renamed --- .../extensible/src/lib/utils/props.util.ts | 27 ++++++++----------- .../extensible/src/lib/utils/state.util.ts | 4 +-- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/npm/ng-packs/packages/components/extensible/src/lib/utils/props.util.ts b/npm/ng-packs/packages/components/extensible/src/lib/utils/props.util.ts index fa6f60eec1..7f06a9b1de 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/utils/props.util.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/utils/props.util.ts @@ -51,22 +51,13 @@ export function mergeWithDefaultProps>( }); } -function isPolicyMet( - checkFunction: (item: string) => boolean, - requiresAll: boolean, - items?: string[], -): boolean { - if (!items?.length) { - return true; - } - return requiresAll ? items.every(checkFunction) : items.some(checkFunction); -} - -export function checkPolicyProperties( +export function checkPolicies( properties: ObjectExtensions.EntityExtensionProperties, configState: ConfigStateService, permissionService: PermissionService, ) { + const props = Object.entries(properties); + const checkPolicy = (policy: Policy): boolean => { const { permissions, globalFeatures, features } = policy; @@ -88,12 +79,16 @@ export function checkPolicyProperties( }, ]; - return checks.every(({ items, requiresAll, check }) => - isPolicyMet(check, requiresAll ?? false, items), - ); + return checks.every(({ items, requiresAll, check }) => { + if (!items?.length) { + return true; + } + + return requiresAll ? items.every(check) : items.some(check); + }); }; - Object.entries(properties).forEach(([name, property]) => { + props.forEach(([name, property]) => { if (property.policy && !checkPolicy(property.policy)) { delete properties[name]; } diff --git a/npm/ng-packs/packages/components/extensible/src/lib/utils/state.util.ts b/npm/ng-packs/packages/components/extensible/src/lib/utils/state.util.ts index f6bbdf5b2d..68a97e844c 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/utils/state.util.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/utils/state.util.ts @@ -16,7 +16,7 @@ import { ObjectExtensions } from '../models/object-extensions'; import { PropCallback } from '../models/props'; import { createEnum, createEnumOptions, createEnumValueResolver } from './enum.util'; import { createDisplayNameLocalizationPipeKeyGenerator } from './localization.util'; -import { checkPolicyProperties, createExtraPropertyValueResolver } from './props.util'; +import { checkPolicies, createExtraPropertyValueResolver } from './props.util'; import { createTypeaheadDisplayNameGenerator, createTypeaheadOptions, @@ -100,7 +100,7 @@ export function mapEntitiesToContributors( return acc; } - checkPolicyProperties(properties, configState, permissionService); + checkPolicies(properties, configState, permissionService); const mapPropertiesToContributors = createPropertiesToContributorsMapper( generateDisplayName,