From 20efb3425c726291ecc3352b740fc8cd138aae13 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Wed, 7 Nov 2018 10:53:51 -0700 Subject: [PATCH] SHIBUI-704 Implemented schema definition --- .../resources/i18n/messages_en.properties | 19 + .../dynamic-http-metadata-provider.ts | 51 ++ .../model/dynamic-http.provider.form.ts | 133 ++++ .../schema/provider/dynamic-http.schema.json | 620 ++++++++++++++++++ 4 files changed, 823 insertions(+) create mode 100644 ui/src/app/metadata/domain/model/providers/dynamic-http-metadata-provider.ts create mode 100644 ui/src/app/metadata/provider/model/dynamic-http.provider.form.ts create mode 100644 ui/src/assets/schema/provider/dynamic-http.schema.json diff --git a/backend/src/main/resources/i18n/messages_en.properties b/backend/src/main/resources/i18n/messages_en.properties index 3d4670252..7df519fff 100644 --- a/backend/src/main/resources/i18n/messages_en.properties +++ b/backend/src/main/resources/i18n/messages_en.properties @@ -74,6 +74,8 @@ value.entity-attributes-filter=EntityAttributes Filter value.spdescriptor=SPSSODescriptor value.attr-auth-descriptor=AttributeAuthorityDescriptor +value.dynamic-http-metadata-provider=DynamicHttpMetadataProvider + brand.header.title=Source Management brand.logo-link-label=Shibboleth brand.logo-link-description=Link to Shibboleth Website @@ -332,6 +334,15 @@ label.attribute-eduPersonUniqueId=eduPersonUniqueId label.attribute-employeeNumber=employeeNumber label.force-authn=Force AuthN +label.dynamic-attributes=Dynamic Attributes +label.min-cache-duration=Min Cache Duration +label.max-cache-duration=Max Cache Duration +label.max-idle-entity-data=Max Idle Entity Data +label.cleanup-task-interval=Cleanup Task Interval +label.persistent-cache-manager-directory=Persistent Cache Manager Directory +label.initialize-from-persistent-cache-in-background=Initialize from Persistent Cache in Background? +label.background-init-from-cache-delay=Background Initialization from Cache Delay + message.must-be-unique=Must be unique. message.name-must-be-unique=Name must be unique. message.uri-valid-format=URI must be valid format. @@ -453,3 +464,11 @@ tooltip.expiration-warning-threshold=For each attempted metadata refresh (whethe tooltip.filter-name=Filter Name tooltip.enable-filter=Enable Filter? tooltip.enable-service=Enable Service? + +tooltip.min-cache-duration=The minimum duration for which metadata will be cached before it is refreshed. +tooltip.max-cache-duration=The maximum duration for which metadata will be cached before it is refreshed. +tooltip.max-idle-entity-data=The maximum duration for which metadata will be allowed to be idle (no requests for it) before it is removed from the cache. +tooltip.cleanup-task-interval=The interval at which the internal cleanup task should run. This task performs background maintenance tasks, such as the removal of expired and idle metadata. +tooltip.persistent-cache-manager-directory=The optional manager for the persistent cache store for resolved metadata. On metadata provider initialization, data present in the persistent cache will be loaded to memory, effectively restoring the state of the provider as closely as possible to that which existed before the previous shutdown. Each individual cache entry will only be loaded if 1) the entry is still valid as determined by the internal provider logic, and 2) the entry passes the (optional) predicate supplied via initializationFromCachePredicateRef. +tooltip.initialize-from-persistent-cache-in-background=Flag indicating whether should initialize from the persistent cache in the background. Initializing from the cache in the background will improve IdP startup times. +tooltip.background-init-from-cache-delay=The delay after which to schedule the background initialization from the persistent cache when initializeFromPersistentCacheInBackground=true. diff --git a/ui/src/app/metadata/domain/model/providers/dynamic-http-metadata-provider.ts b/ui/src/app/metadata/domain/model/providers/dynamic-http-metadata-provider.ts new file mode 100644 index 000000000..5d3201836 --- /dev/null +++ b/ui/src/app/metadata/domain/model/providers/dynamic-http-metadata-provider.ts @@ -0,0 +1,51 @@ +import { BaseMetadataProvider } from './base-metadata-provider'; + +export interface DynamicHttpMetadataProvider extends BaseMetadataProvider { + id: string; + metadataURL: string; + dynamicMetadataResolverAttributes: DynamicMetadataResolverAttributes; + httpMetadataResolverAttributes: HttpMetadataResolverAttributes; + maxConnectionsTotal: number; + maxConnectionsPerRoute: number; + supportedContentTypes: string[]; +} + +export interface DynamicMetadataResolverAttributes { + refreshDelayFactor: number; + minCacheDuration: string; + maxCacheDuration: string; + maxIdleEntityData: string; + removeIdleEntityData: boolean; + cleanupTaskInterval: string; + + persistentCacheManagerRef: string; + persistentCacheManagerDirectory: string; + persistentCacheKeyGeneratorRef: string; + initializeFromPersistentCacheInBackground: boolean; + backgroundInitializationFromCacheDelay: string; + initializationFromCachePredicateRef: string; +} + +export interface HttpMetadataResolverAttributes { + httpClientRef; + connectionRequestTimeout: string; + connectionTimeout: string; + socketTimeout: string; + disregardTLSCertificate: boolean; + tlsTrustEngineRef: string; + httpClientSecurityParametersRef: string; + proxyHost: string; + proxyPort: string; + proxyUser: string; + proxyPassword: string; + httpCaching: HttpCachingType; + httpCacheDirectory: string; + httpMaxCacheEntries: number; + httpMaxCacheEntrySize: number; +} + +export enum HttpCachingType { + NONE = 'none', + FILE = 'file', + MEMORY = 'memory' +} diff --git a/ui/src/app/metadata/provider/model/dynamic-http.provider.form.ts b/ui/src/app/metadata/provider/model/dynamic-http.provider.form.ts new file mode 100644 index 000000000..4baf5bfe8 --- /dev/null +++ b/ui/src/app/metadata/provider/model/dynamic-http.provider.form.ts @@ -0,0 +1,133 @@ +import { Wizard } from '../../../wizard/model'; +import { DynamicHttpMetadataProvider } from '../../domain/model/providers/dynamic-http-metadata-provider'; +import { BaseMetadataProviderEditor } from './base.provider.form'; +import UriValidator from '../../../shared/validation/uri.validator'; + +export const DynamicHttpMetadataProviderWizard: Wizard = { + ...BaseMetadataProviderEditor, + label: 'DynamicHttpMetadataProvider', + type: 'DynamicHttpMetadataResolver', + getValidators(namesList: string[] = [], xmlIdList: string[] = []): any { + const validators = BaseMetadataProviderEditor.getValidators(namesList); + validators['/xmlId'] = (value, property, form) => { + const err = xmlIdList.indexOf(value) > -1 ? { + code: 'INVALID_ID', + path: `#${property.path}`, + message: 'message.id-unique', + params: [value] + } : null; + return err; + }; + validators['/metadataURL'] = (value, property, form) => { + return !UriValidator.isUri(value) ? { + code: 'INVALID_URI', + path: `#${property.path}`, + message: 'message.uri-valid-format', + params: [value] + } : null; + }; + + return validators; + }, + steps: [ + { + id: 'common', + label: 'label.common-attributes', + index: 2, + initialValues: [], + schema: 'assets/schema/provider/dynamic-http.schema.json', + fields: [ + 'xmlId', + 'metadataURL', + 'requireValidMetadata', + 'failFastInitialization' + ] + }, + { + id: 'dynamic', + label: 'label.dynamic-attributes', + index: 3, + initialValues: [], + schema: 'assets/schema/provider/dynamic-http.schema.json', + fields: [ + 'dynamicMetadataResolverAttributes' + ] + }, + { + id: 'plugins', + label: 'label.metadata-filter-plugins', + index: 4, + initialValues: [ + { key: 'metadataFilters', value: [] } + ], + schema: 'assets/schema/provider/dynamic-http.schema.json', + fields: [ + 'metadataFilters' + ] + }, + { + id: 'summary', + label: 'label.finished', + index: 5, + initialValues: [], + schema: 'assets/schema/provider/dynamic-http.schema.json', + fields: [ + 'enabled' + ] + } + ] +}; + + +export const DynamicHttpMetadataProviderEditor: Wizard = { + ...DynamicHttpMetadataProviderWizard, + steps: [ + { + id: 'common', + label: 'label.common-attributes', + index: 1, + initialValues: [], + schema: 'assets/schema/provider/dynamic-http.schema.json', + fields: [ + 'enabled', + 'xmlId', + 'metadataURL', + 'requireValidMetadata', + 'failFastInitialization' + ] + }, + { + id: 'dynamic', + label: 'label.dynamic-attributes', + index: 3, + initialValues: [], + schema: 'assets/schema/provider/dynamic-http.schema.json', + fields: [ + 'dynamicMetadataResolverAttributes' + ] + }, + { + id: 'plugins', + label: 'label.metadata-filter-plugins', + index: 4, + initialValues: [ + { key: 'metadataFilters', value: [] } + ], + schema: 'assets/schema/provider/dynamic-http.schema.json', + fields: [ + 'metadataFilters' + ] + }, + { + id: 'advanced', + label: 'label.advanced-settings', + index: 4, + initialValues: [], + locked: true, + schema: 'assets/schema/provider/filebacked-http-advanced.schema.json', + fields: [ + 'httpMetadataResolverAttributes' + ] + } + ] +}; diff --git a/ui/src/assets/schema/provider/dynamic-http.schema.json b/ui/src/assets/schema/provider/dynamic-http.schema.json new file mode 100644 index 000000000..231078877 --- /dev/null +++ b/ui/src/assets/schema/provider/dynamic-http.schema.json @@ -0,0 +1,620 @@ +{ + "type": "object", + "order": [ + "xmlId", + "metadataURL" + ], + "required": [ + "xmlId", + "metadataURL" + ], + "properties": { + "enabled": { + "title": "label.enable-provider-upon-saving", + "description": "tooltip.enable-provider-upon-saving", + "type": "boolean", + "default": false + }, + "xmlId": { + "title": "label.xml-id", + "description": "tooltip.xml-id", + "type": "string", + "default": "", + "minLength": 1 + }, + "metadataURL": { + "title": "label.metadata-url", + "description": "tooltip.metadata-url", + "type": "string", + "default": "", + "minLength": 1 + }, + "requireValidMetadata": { + "title": "label.require-valid-metadata", + "description": "tooltip.require-valid-metadata", + "type": "boolean", + "widget": { + "id": "boolean-radio" + }, + "oneOf": [ + { + "enum": [ + true + ], + "description": "value.true" + }, + { + "enum": [ + false + ], + "description": "value.false" + } + ], + "default": true + }, + "failFastInitialization": { + "title": "label.fail-fast-init", + "description": "tooltip.fail-fast-init", + "type": "boolean", + "widget": { + "id": "boolean-radio" + }, + "oneOf": [ + { + "enum": [ + true + ], + "description": "value.true" + }, + { + "enum": [ + false + ], + "description": "value.false" + } + ], + "default": true + }, + "dynamicMetadataResolverAttributes": { + "type": "object", + "properties": { + "refreshDelayFactor": { + "title": "label.refresh-delay-factor", + "description": "tooltip.refresh-delay-factor", + "type": "number", + "widget": { + "id": "number", + "step": 0.01 + }, + "placeholder": "label.real-number", + "minimum": 0, + "maximum": 1, + "default": null + }, + "minCacheDuration": { + "title": "label.min-cache-duration", + "description": "tooltip.min-cache-duration", + "type": "string", + "placeholder": "label.duration", + "widget": { + "id": "datalist", + "data": [ + "PT0S", + "PT30S", + "PT1M", + "PT10M", + "PT30M", + "PT1H", + "PT4H", + "PT12H", + "PT24H" + ] + }, + "default": null, + "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" + }, + "maxCacheDuration": { + "title": "label.max-cache-duration", + "description": "tooltip.max-cache-duration", + "type": "string", + "placeholder": "label.duration", + "widget": { + "id": "datalist", + "data": [ + "PT0S", + "PT30S", + "PT1M", + "PT10M", + "PT30M", + "PT1H", + "PT4H", + "PT12H", + "PT24H" + ] + }, + "default": null, + "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" + }, + "maxIdleEntityData": { + "title": "label.max-idle-entity-data", + "description": "tooltip.max-idle-entity-data", + "type": "string", + "placeholder": "label.duration", + "widget": { + "id": "datalist", + "data": [ + "PT0S", + "PT30S", + "PT1M", + "PT10M", + "PT30M", + "PT1H", + "PT4H", + "PT12H", + "PT24H" + ] + }, + "default": null, + "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" + }, + "cleanupTaskInterval": { + "title": "label.cleanup-task-interval", + "description": "tooltip.cleanup-task-interval", + "type": "string", + "placeholder": "label.duration", + "widget": { + "id": "datalist", + "data": [ + "PT0S", + "PT30S", + "PT1M", + "PT10M", + "PT30M", + "PT1H", + "PT4H", + "PT12H", + "PT24H" + ] + }, + "default": null, + "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" + }, + "persistentCacheManagerDirectory": { + "title": "label.persistent-cache-manager-directory", + "description": "tooltip.persistent-cache-manager-directory", + "type": "string", + "default": "", + "minLength": 1 + }, + "initializeFromPersistentCacheInBackground": { + "title": "label.initialize-from-persistent-cache-in-background", + "description": "tooltip.initialize-from-persistent-cache-in-background", + "type": "boolean", + "widget": { + "id": "boolean-radio" + }, + "oneOf": [ + { + "enum": [ + true + ], + "description": "value.true" + }, + { + "enum": [ + false + ], + "description": "value.false" + } + ], + "default": true + }, + "backgroundInitializationFromCacheDelay": { + "title": "label.background-init-from-cache-delay", + "description": "tooltip.background-init-from-cache-delay", + "type": "string", + "placeholder": "label.duration", + "widget": { + "id": "datalist", + "data": [ + "PT0S", + "PT30S", + "PT1M", + "PT10M", + "PT30M", + "PT1H", + "PT4H", + "PT12H", + "PT24H" + ] + }, + "visibleIf": { + "initializeFromPersistentCacheInBackground": [ + true + ] + }, + "default": null, + "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" + } + } + }, + "httpMetadataResolverAttributes": { + "order": [], + "type": "object", + "fieldsets": [ + { + "title": "label.http-security-attributes", + "type": "section", + "class": "col-12", + "fields": [ + "disregardTLSCertificate" + ] + }, + { + "title": "label.http-connection-attributes", + "type": "section", + "fields": [ + "connectionRequestTimeout", + "connectionTimeout", + "socketTimeout" + ] + }, + { + "title": "label.http-proxy-attributes", + "type": "section", + "class": "col-12", + "fields": [ + "proxyHost", + "proxyPort", + "proxyUser", + "proxyPassword" + ] + }, + { + "title": "label.http-caching-attributes", + "type": "section", + "class": "col-12", + "fields": [ + "httpCaching", + "httpCacheDirectory", + "httpMaxCacheEntries", + "httpMaxCacheEntrySize" + ] + }, + { + "title": "", + "type": "hidden", + "class": "col-12", + "fields": [ + "tlsTrustEngineRef", + "httpClientSecurityParametersRef", + "httpClientRef" + ] + } + ], + "properties": { + "disregardTLSCertificate": { + "type": "boolean", + "title": "label.disregard-tls-cert", + "description": "tooltip.disregard-tls-cert", + "widget": { + "id": "boolean-radio" + }, + "oneOf": [ + { + "enum": [ + true + ], + "description": "True" + }, + { + "enum": [ + false + ], + "description": "False" + } + ], + "default": false + }, + "httpClientRef": { + "type": "string", + "title": "", + "description": "", + "placeholder": "", + "widget": "hidden", + "default": "" + }, + "connectionRequestTimeout": { + "type": "string", + "title": "label.connection-request-timeout", + "description": "tooltip.connection-request-timeout", + "placeholder": "label.duration", + "widget": { + "id": "datalist", + "data": [ + "PT0S", + "PT30S", + "PT1M", + "PT10M", + "PT30M", + "PT1H", + "PT4H", + "PT12H", + "PT24H" + ] + }, + "default": null, + "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" + }, + "connectionTimeout": { + "type": "string", + "title": "label.connection-timeout", + "description": "tooltip.connection-timeout", + "placeholder": "label.duration", + "widget": { + "id": "datalist", + "data": [ + "PT0S", + "PT30S", + "PT1M", + "PT10M", + "PT30M", + "PT1H", + "PT4H", + "PT12H", + "PT24H" + ] + }, + "default": null, + "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" + }, + "socketTimeout": { + "type": "string", + "title": "label.socket-timeout", + "description": "tooltip.socket-timeout", + "placeholder": "label.duration", + "widget": { + "id": "datalist", + "data": [ + "PT0S", + "PT30S", + "PT1M", + "PT10M", + "PT30M", + "PT1H", + "PT4H", + "PT12H", + "PT24H" + ] + }, + "default": null, + "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" + }, + "tlsTrustEngineRef": { + "type": "string", + "title": "", + "description": "", + "placeholder": "", + "widget": "hidden", + "default": "" + }, + "httpClientSecurityParametersRef": { + "type": "string", + "title": "", + "description": "", + "placeholder": "", + "widget": "hidden", + "default": "" + }, + "proxyHost": { + "type": "string", + "title": "label.proxy-host", + "description": "tooltip.proxy-host", + "placeholder": "", + "default": "" + }, + "proxyPort": { + "type": "string", + "title": "label.proxy-port", + "description": "tooltip.proxy-port", + "placeholder": "", + "default": "" + }, + "proxyUser": { + "type": "string", + "title": "label.proxy-user", + "description": "tooltip.proxy-user", + "placeholder": "", + "default": "" + }, + "proxyPassword": { + "type": "string", + "title": "label.proxy-password", + "description": "tooltip.proxy-password", + "placeholder": "", + "default": "" + }, + "httpCaching": { + "type": "string", + "title": "label.http-caching", + "description": "tooltip.http-caching", + "placeholder": "label.select-caching-type", + "widget": { + "id": "select" + }, + "oneOf": [ + { + "enum": [ + "none" + ], + "description": "value.none" + }, + { + "enum": [ + "file" + ], + "description": "value.file" + }, + { + "enum": [ + "memory" + ], + "description": "value.memory" + } + ] + }, + "httpCacheDirectory": { + "type": "string", + "title": "label.http-caching-directory", + "description": "tooltip.http-caching-directory", + "placeholder": "" + }, + "httpMaxCacheEntries": { + "type": "integer", + "title": "label.http-max-cache-entries", + "description": "tooltip.http-max-cache-entries", + "placeholder": "", + "default": 0, + "minimum": 0 + }, + "httpMaxCacheEntrySize": { + "type": "integer", + "title": "label.max-cache-entry-size", + "description": "tooltip.max-cache-entry-size", + "placeholder": "", + "default": 0, + "minimum": 0 + } + } + }, + "metadataFilters": { + "title": "", + "description": "", + "type": "object", + "properties": { + "RequiredValidUntil": { + "title": "label.required-valid-until", + "type": "object", + "widget": { + "id": "fieldset" + }, + "properties": { + "maxValidityInterval": { + "title": "label.max-validity-interval", + "description": "tooltip.max-validity-interval", + "type": "string", + "placeholder": "label.duration", + "widget": { + "id": "datalist", + "data": [ + "PT0S", + "PT30S", + "PT1M", + "PT10M", + "PT30M", + "PT1H", + "PT4H", + "PT12H", + "PT24H" + ] + }, + "default": null, + "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" + } + } + }, + "SignatureValidation": { + "title": "label.signature-validation-filter", + "type": "object", + "widget": { + "id": "fieldset" + }, + "properties": { + "requireSignedRoot": { + "title": "label.require-signed-root", + "description": "tooltip.require-signed-root", + "type": "boolean", + "default": true + }, + "certificateFile": { + "title": "label.certificate-file", + "description": "tooltip.certificate-file", + "type": "string", + "widget": "textarea", + "default": "" + } + }, + "anyOf": [ + { + "properties": { + "requireSignedRoot": { + "enum": [ + true + ] + } + }, + "required": [ + "certificateFile" + ] + }, + { + "properties": { + "requireSignedRoot": { + "enum": [ + false + ] + } + } + } + ] + }, + "EntityRoleWhiteList": { + "title": "label.entity-role-whitelist", + "type": "object", + "widget": { + "id": "fieldset" + }, + "properties": { + "retainedRoles": { + "title": "label.retained-roles", + "description": "tooltip.retained-roles", + "type": "array", + "items": { + "widget": { + "id": "select" + }, + "type": "string", + "oneOf": [ + { + "enum": [ + "SPSSODescriptor" + ], + "description": "value.spdescriptor" + }, + { + "enum": [ + "AttributeAuthorityDescriptor" + ], + "description": "value.attr-auth-descriptor" + } + ] + } + }, + "removeRolelessEntityDescriptors": { + "title": "label.remove-roleless-entity-descriptors", + "description": "tooltip.remove-roleless-entity-descriptors", + "type": "boolean", + "default": true + }, + "removeEmptyEntitiesDescriptors": { + "title": "label.remove-empty-entities-descriptors", + "description": "tooltip.remove-empty-entities-descriptors", + "type": "boolean", + "default": true + } + } + } + } + } + } +} \ No newline at end of file