UNPKG

@microsoft/windows-admin-center-sdk

Version:

Microsoft - Windows Admin Center Shell

1 lines 35.7 kB
{"version":3,"sources":["../../../packages/core/security/tool-condition-validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,UAAU,EAAM,MAAM,MAAM,CAAC;AAE7C,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAMjD,OAAO,EAIH,2BAA2B,EAC3B,kDAAkD,EAGrD,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AAWxE;;GAEG;AACH,0BAAkB,mBAAmB;IACjC;;OAEG;IACH,aAAa,IAAA;IAEb;;OAEG;IACH,QAAQ,IAAA;IAER;;OAEG;IACH,OAAO,IAAA;IAEP;;OAEG;IACH,MAAM,IAAA;IAEN;;OAEG;IACH,MAAM,IAAA;CACT;AAED;;GAEG;AACH,MAAM,WAAW,6BAA6B;IAC1C;;OAEG;IACH,MAAM,EAAE,mBAAmB,CAAC;IAE5B;;OAEG;IACH,MAAM,EAAE,UAAU,CAAC,kDAAkD,CAAC,GAAG,kDAAkD,CAAC;CAC/H;AAED;;;GAGG;AACH,qBAAa,sBAAsB;IA6MnB,OAAO,CAAC,UAAU;IA5M9B;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,yBAAyB,CAgGtC;IAEF;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,SAAS,CAsEtB;IAEF,OAAO,CAAC,MAAM,CAAC,eAAe,CAAyB;IAEvD,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,YAAY,CAAyD;IAE7E;;;;;OAKG;WACW,OAAO,CAAC,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,oBAAoB,GAAG,sBAAsB;IAQnG;;;;;OAKG;gBACiB,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,oBAAoB;IAMxE;;;;;;;;OAQG;IACI,iBAAiB,CACpB,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,2BAA2B,EACrC,IAAI,EAAE,2BAA2B,GAAG,6BAA6B;IAuKrE,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,wBAAwB;IA0BhC,OAAO,CAAC,iBAAiB;IAmBzB,OAAO,CAAC,gBAAgB;IAgBxB,OAAO,CAAC,qBAAqB;IA0B7B,OAAO,CAAC,6BAA6B;IAWrC,OAAO,CAAC,cAAc;IAuKtB,OAAO,CAAC,cAAc;IAmDtB,OAAO,CAAC,eAAe;CAI1B","file":"tool-condition-validator.d.ts","sourcesContent":["import { EMPTY, Observable, of } from 'rxjs';\r\nimport { catchError, expand, filter, map, take } from 'rxjs/operators';\r\nimport { AppContext } from '../data/app-context';\r\nimport { Net } from '../data/net';\r\nimport { PowerShell, PowerShellCommand } from '../data/powershell';\r\nimport { LogLevel } from '../diagnostics/log-level';\r\nimport { Logging } from '../diagnostics/logging';\r\nimport { Strings } from '../generated/strings';\r\nimport {\r\n EnvironmentModule,\r\n EnvironmentModuleConditionMap,\r\n EnvironmentModuleConditionStatement,\r\n EnvironmentModuleEntryPoint,\r\n EnvironmentModuleEntryPointWithToolConditionResult,\r\n EnvironmentModuleToolConditionResult,\r\n EnvironmentModuleToolState\r\n} from '../manifest/environment-modules';\r\nimport { Connection } from '../security/connection';\r\nimport { GatewayMode } from '../shared/gateway-inventory/gateway-inventory';\r\nimport { InventoryQueryCaches } from '../shared/inventory-query-caches';\r\nimport { ServerInventory } from '../shared/server-inventory/server-inventory';\r\nimport { ToolInventoryCache } from '../shared/tool-inventory/tool-inventory-cache';\r\n\r\n/**\r\n * The interface object to be used by expand calculation.\r\n */\r\ninterface ToolConditionExpandResult extends EnvironmentModuleToolConditionResult {\r\n ends: boolean;\r\n}\r\n\r\n/**\r\n * The calculation weight ratio base on performance.\r\n */\r\nexport const enum ToolConditionWeight {\r\n /**\r\n * Not using any observable calculation.\r\n */\r\n NonObservable,\r\n\r\n /**\r\n * Using the connection property.\r\n */\r\n Property,\r\n\r\n /**\r\n * Using the gateway inventory data.\r\n */\r\n Gateway,\r\n\r\n /**\r\n * Using the server inventory data.\r\n */\r\n Server,\r\n\r\n /**\r\n * Using a custom PowerShell script.\r\n */\r\n Script\r\n}\r\n\r\n/**\r\n * The condition result with weight information.\r\n */\r\nexport interface ToolConditionResultWithWeight {\r\n /**\r\n * The calculation weight.\r\n */\r\n weight: ToolConditionWeight;\r\n\r\n /**\r\n * The result data.\r\n */\r\n result: Observable<EnvironmentModuleEntryPointWithToolConditionResult> | EnvironmentModuleEntryPointWithToolConditionResult;\r\n}\r\n\r\n/**\r\n * The class handles conditions of tools to be presented on tools' menu.\r\n * @dynamic\r\n */\r\nexport class ToolConditionValidator {\r\n /**\r\n * Support the following condition name.\r\n * It can be a string, number, boolean and version string.\r\n */\r\n private static serverInventoryProperties: { conditionName: string, dataName: string }[] = [\r\n // string\r\n {\r\n conditionName: 'computerManufacturer',\r\n dataName: 'computerManufacturer'\r\n },\r\n\r\n // string\r\n {\r\n conditionName: 'computerModel',\r\n dataName: 'computerModel'\r\n },\r\n\r\n // number\r\n {\r\n conditionName: 'operatingSystemSKU',\r\n dataName: 'operatingSystemSKU'\r\n },\r\n\r\n // version string\r\n {\r\n conditionName: 'operatingSystemVersion',\r\n dataName: 'operatingSystemVersion'\r\n },\r\n\r\n // number\r\n {\r\n conditionName: 'windowsProductType',\r\n dataName: 'productType'\r\n },\r\n\r\n // string\r\n {\r\n conditionName: 'clusterFqdn',\r\n dataName: 'clusterFqdn'\r\n },\r\n\r\n // string\r\n {\r\n conditionName: 'systemLockdownPolicy',\r\n dataName: 'systemLockdownPolicy'\r\n },\r\n\r\n // boolean\r\n {\r\n conditionName: 'isHyperVRoleInstalled',\r\n dataName: 'isHyperVRoleInstalled'\r\n },\r\n\r\n // boolean\r\n {\r\n conditionName: 'isHyperVPowershellInstalled',\r\n dataName: 'isHyperVPowershellInstalled'\r\n },\r\n\r\n // boolean\r\n {\r\n conditionName: 'isManagementToolsAvailable',\r\n dataName: 'isManagementToolsAvailable'\r\n },\r\n\r\n // boolean\r\n {\r\n conditionName: 'isWmfInstalled',\r\n dataName: 'isWmfInstalled'\r\n },\r\n\r\n // boolean\r\n {\r\n conditionName: 'isRemoteAppEnabled',\r\n dataName: 'isRemoteAppEnabled'\r\n },\r\n\r\n // boolean\r\n {\r\n conditionName: 'isDomainController',\r\n dataName: 'isDomainController'\r\n },\r\n\r\n // boolean\r\n {\r\n conditionName: 'isS2dEnabled',\r\n dataName: 'isS2dEnabled'\r\n },\r\n\r\n // boolean\r\n {\r\n conditionName: 'isBritannicaEnabled',\r\n dataName: 'isBritannicaEnabled'\r\n },\r\n\r\n // boolean\r\n {\r\n conditionName: 'isHciServer',\r\n dataName: 'isHciServer'\r\n }\r\n ];\r\n\r\n /**\r\n * The following operators are supported.\r\n * String comparison uses caseinsesitive pattern.\r\n */\r\n private static operators = {\r\n /**\r\n * Greater than: number, string (caseinsesitive), version\r\n */\r\n gt: 'gt',\r\n\r\n /**\r\n * Greater or equal: number, string (caseinsesitive), version\r\n */\r\n ge: 'ge',\r\n\r\n /**\r\n * Less than: number, string (caseinsesitive), version\r\n */\r\n lt: 'lt',\r\n\r\n /**\r\n * Less or equal: number, string (caseinsesitive), version\r\n */\r\n le: 'le',\r\n\r\n /**\r\n * Equal: number, string (caseinsesitive), version (Accept '*' like \"1.2.*\")\r\n */\r\n eq: 'eq',\r\n\r\n /**\r\n * Not equal: number, string (caseinsesitive), version (Accept '*' like \"1.2.*\")\r\n */\r\n ne: 'ne',\r\n\r\n /**\r\n * Test true: number, string, boolean\r\n */\r\n is: 'is',\r\n\r\n /**\r\n * Test false: number, string, boolean\r\n */\r\n not: 'not',\r\n\r\n /**\r\n * Contains a string: string (caseinsesitive)\r\n */\r\n contains: 'contains',\r\n\r\n /**\r\n * Not contains a string: string (caseinsesitive)\r\n */\r\n notContains: 'notContains',\r\n\r\n /**\r\n * Any one of value (number or string) equal: numberArray, stringArray\r\n */\r\n anyEq: 'anyEq',\r\n\r\n /**\r\n * Any one of value (number or string) not equal: numberArray, stringArray\r\n */\r\n anyNe: 'anyNe',\r\n\r\n /**\r\n * Any one of string contains: stringArray\r\n */\r\n anyContains: 'anyContains',\r\n\r\n /**\r\n * Any one of string not contains: stringArray\r\n */\r\n anyNotContains: 'anyNotContains'\r\n };\r\n\r\n private static internalCurrent: ToolConditionValidator;\r\n\r\n private caches: InventoryQueryCaches;\r\n private toolInventoryCache: ToolInventoryCache;\r\n private errorStrings = MsftSme.getStrings<Strings>().MsftSmeShell.Core.Error;\r\n\r\n /**\r\n * Gets the current object of the ToolConditionValidator class, and maintains as singleton.\r\n *\r\n * @param appContext the application context.\r\n * @param caches the instance of the inventory query caches to share the resource.\r\n */\r\n public static current(appContext: AppContext, caches: InventoryQueryCaches): ToolConditionValidator {\r\n if (!ToolConditionValidator.internalCurrent) {\r\n ToolConditionValidator.internalCurrent = new ToolConditionValidator(appContext, caches);\r\n }\r\n\r\n return ToolConditionValidator.internalCurrent;\r\n }\r\n\r\n /**\r\n * Initializes a new instance of the ToolConditionValidator class.\r\n *\r\n * @param appContext the application context.\r\n * @param caches the instance of the inventory query caches to share the resource.\r\n */\r\n constructor(private appContext: AppContext, caches: InventoryQueryCaches) {\r\n const statusExpiration = 4 * 60 * 1000; // 4 min\r\n this.caches = caches;\r\n this.toolInventoryCache = new ToolInventoryCache(appContext, { expiration: statusExpiration });\r\n }\r\n\r\n /**\r\n * Scan the tool condition to be present or not.\r\n *\r\n * @param connection the connection object.\r\n * @param solution The entry point object of solution.\r\n * @param tool The entry point object of tool.\r\n * @param scanMode The mode of scanning.\r\n * @return the result observable.\r\n */\r\n public scanToolCondition(\r\n connection: Connection,\r\n solution: EnvironmentModuleEntryPoint,\r\n tool: EnvironmentModuleEntryPoint): ToolConditionResultWithWeight {\r\n\r\n if (!tool.requirements) {\r\n // tool is missing requirements, never show.\r\n return {\r\n weight: ToolConditionWeight.NonObservable,\r\n result: {\r\n ...tool,\r\n ...<EnvironmentModuleToolConditionResult>{\r\n show: false,\r\n detail: EnvironmentModuleToolState.NotSupported\r\n }\r\n }\r\n };\r\n }\r\n\r\n const solutionId = EnvironmentModule.createFormattedEntrypoint(solution);\r\n const toolId = EnvironmentModule.createFormattedEntrypoint(tool);\r\n const checkersCollection = [];\r\n let weight = ToolConditionWeight.NonObservable;\r\n for (const requirement of tool.requirements) {\r\n const checkers: Observable<EnvironmentModuleToolConditionResult>[] = [];\r\n\r\n // tool must specify the solutions it can show in\r\n if (!requirement.solutionIds || requirement.solutionIds.every(id => id !== solutionId)) {\r\n continue;\r\n }\r\n\r\n // if ths solution is a connections type, then the tool must specify the connection type\r\n if (solution.rootNavigationBehavior === 'connections'\r\n && (!requirement.connectionTypes || requirement.connectionTypes.every(type => type !== connection.type))) {\r\n continue;\r\n }\r\n\r\n // if there are no conditions to check, then this tool has been satisfied.\r\n if (!requirement.conditions || requirement.conditions.length === 0) {\r\n checkers.push(this.installationTypeValidate(connection));\r\n weight = Math.max(weight, ToolConditionWeight.Gateway);\r\n } else {\r\n for (const condition of requirement.conditions) {\r\n\r\n checkers.push(this.installationTypeValidate(connection, condition.installationTypes));\r\n weight = Math.max(weight, ToolConditionWeight.Gateway);\r\n\r\n if (condition.localhost !== undefined && !condition.localhost) {\r\n // if connection is localhost and not supported.\r\n checkers.push(this.localhostValidate(connection));\r\n weight = Math.max(weight, ToolConditionWeight.Gateway);\r\n }\r\n\r\n if (condition.inventory) {\r\n checkers.push(this.inventoryValidate(connection, condition.inventory));\r\n weight = Math.max(weight, ToolConditionWeight.Server);\r\n }\r\n\r\n if (condition.properties) {\r\n checkers.push(this.propertyValidate(connection, condition.properties));\r\n weight = Math.max(weight, ToolConditionWeight.Property);\r\n }\r\n\r\n if (condition.powerShell) {\r\n // powerShell { command, script }\r\n checkers.push(this.toolInventoryValidate(connection, toolId, condition.powerShell));\r\n weight = Math.max(weight, ToolConditionWeight.Script);\r\n } else if (condition.script) {\r\n // deprecated\r\n checkers.push(this.toolInventoryValidate(connection, toolId, condition.script));\r\n weight = Math.max(weight, ToolConditionWeight.Script);\r\n }\r\n }\r\n }\r\n\r\n checkersCollection.push(checkers);\r\n }\r\n\r\n if (checkersCollection.length === 0) {\r\n return {\r\n weight: ToolConditionWeight.NonObservable,\r\n result: {\r\n ...tool,\r\n ...<EnvironmentModuleToolConditionResult>{\r\n show: false,\r\n detail: EnvironmentModuleToolState.NotSupported,\r\n connectionId: connection ? connection.id : null\r\n }\r\n }\r\n };\r\n }\r\n\r\n let collectionIndex = 0;\r\n let checkerIndex = 0;\r\n let lastDetail: EnvironmentModuleToolState = null;\r\n let lastMessage: string = null;\r\n return {\r\n weight: weight,\r\n result: this.runChecker(checkersCollection[collectionIndex][checkerIndex])\r\n .pipe(catchError(() => {\r\n Logging.log({\r\n level: LogLevel.Error,\r\n message: this.errorStrings.ToolValidationResult.message.format(\r\n tool.parentModule.name,\r\n tool.displayName),\r\n source: 'ToolConditionValidator'\r\n });\r\n return of(<ToolConditionExpandResult>{\r\n show: false,\r\n ends: true\r\n });\r\n }),\r\n expand((value) => {\r\n checkerIndex++;\r\n if (value.ends) {\r\n return EMPTY;\r\n }\r\n\r\n if (value.detail !== undefined && lastDetail == null) {\r\n lastDetail = value.detail;\r\n }\r\n\r\n if (value.message !== undefined && lastMessage == null) {\r\n lastMessage = value.message;\r\n }\r\n\r\n if (!value.show) {\r\n // failed result. increment collection index and reset checkerIndex.\r\n if (checkersCollection.length > ++collectionIndex) {\r\n // still has another condition, try next checker set.\r\n return this.runChecker(checkersCollection[collectionIndex][checkerIndex = 0]);\r\n } else {\r\n // no more condition, end to return 'false'.\r\n return of(<ToolConditionExpandResult>{\r\n show: false,\r\n detail: lastDetail,\r\n message: lastMessage,\r\n ends: true\r\n });\r\n }\r\n }\r\n\r\n if (checkersCollection[collectionIndex].length > checkerIndex) {\r\n // check next checker.\r\n return this.runChecker(checkersCollection[collectionIndex][checkerIndex]);\r\n } else {\r\n // all state/succeeded within the checker set.\r\n return of({\r\n show: true,\r\n detail: lastDetail,\r\n message: lastMessage,\r\n ends: true\r\n });\r\n }\r\n }),\r\n filter(combined => combined.ends),\r\n map(combined => {\r\n return {\r\n ...tool,\r\n ...<EnvironmentModuleToolConditionResult>{\r\n show: combined.show,\r\n detail: combined.detail,\r\n message: combined.message,\r\n connectionId: connection ? connection.id : null\r\n }\r\n };\r\n }))\r\n };\r\n }\r\n\r\n private runChecker(checker: Observable<EnvironmentModuleToolConditionResult>): Observable<ToolConditionExpandResult> {\r\n return checker.pipe(\r\n take(1),\r\n map(result => <ToolConditionExpandResult>{ ...result, ends: false }));\r\n }\r\n\r\n private localhostValidate(\r\n connection: Connection): Observable<EnvironmentModuleToolConditionResult> {\r\n return this.caches.gatewayCache.createObservable({})\r\n .pipe(\r\n map(instance => <EnvironmentModuleToolConditionResult>{\r\n show: !(instance.mode !== GatewayMode.Service\r\n && connection.properties\r\n && connection.properties.displayName === 'localhost')\r\n }));\r\n }\r\n\r\n private installationTypeValidate(\r\n connection: Connection, installationType?: String[]): Observable<EnvironmentModuleToolConditionResult> {\r\n if (MsftSme.getValue<boolean>(MsftSme.self().Environment.configuration, 'gateway.disabled')) {\r\n return of({ show: true });\r\n }\r\n\r\n if (!installationType || !Array.isArray(installationType) || installationType.length === 0) {\r\n // Default to standard if property doesnt exist in the manifest.\r\n installationType = ['Standard'];\r\n }\r\n\r\n return this.caches.gatewayCache.createObservable({ params: {} })\r\n .pipe(\r\n catchError((error) => {\r\n const errorStrings = MsftSme.getStrings<Strings>().MsftSmeShell.Core.Error;\r\n const notification = this.appContext.notification.create(connection.name);\r\n notification.showError(\r\n errorStrings.ToolValidationGatewayInventoryError.title,\r\n errorStrings.ToolValidationGatewayInventoryError.message.format(Net.getErrorMessage(error)));\r\n return of(null);\r\n }),\r\n map(instance => <EnvironmentModuleToolConditionResult>{\r\n show: installationType.some(t => instance.installationType === t)\r\n }));\r\n }\r\n\r\n private inventoryValidate(\r\n connection: Connection, condition: EnvironmentModuleConditionMap): Observable<EnvironmentModuleToolConditionResult> {\r\n const nodeName = connection.activeAlias ? connection.activeAlias : connection.name;\r\n return this.caches.serverCache.createObservable({ params: { name: nodeName } })\r\n .pipe(\r\n catchError((error) => {\r\n const errorStrings = MsftSme.getStrings<Strings>().MsftSmeShell.Core.Error;\r\n const notification = this.appContext.notification.create(connection.name);\r\n notification.showError(\r\n errorStrings.ToolValidationInventoryError.title,\r\n errorStrings.ToolValidationInventoryError.message.format(Net.getErrorMessage(error)));\r\n return of(null);\r\n }),\r\n filter((instance: ServerInventory) => instance.serverName === nodeName),\r\n map(instance => <EnvironmentModuleToolConditionResult>{\r\n show: this.checkServerInventoryCondition(condition, instance)\r\n }));\r\n }\r\n\r\n private propertyValidate(\r\n connection: Connection, condition: EnvironmentModuleConditionMap): Observable<EnvironmentModuleToolConditionResult> {\r\n const propertyNames = Object.keys(condition);\r\n let result = true;\r\n for (const propertyName of propertyNames) {\r\n const propertyValue = connection.properties && connection.properties[propertyName];\r\n const conditionItem = condition[propertyName];\r\n if (conditionItem && !this.checkCondition(propertyValue, conditionItem)) {\r\n result = false;\r\n break;\r\n }\r\n }\r\n\r\n return of(<EnvironmentModuleToolConditionResult>{ show: result });\r\n }\r\n\r\n private toolInventoryValidate(\r\n connection: Connection,\r\n id: string,\r\n scriptOrCommand: string | PowerShellCommand): Observable<EnvironmentModuleToolConditionResult> {\r\n const command = PowerShell.getPowerShellCommand(scriptOrCommand);\r\n return this.toolInventoryCache.query(\r\n { ...{ name: connection.activeAlias ? connection.activeAlias : connection.name, id: id }, ...command })\r\n .pipe(\r\n map(inventory => <EnvironmentModuleToolConditionResult>{\r\n show: inventory.instance.state === EnvironmentModuleToolState.Available\r\n || inventory.instance.state === EnvironmentModuleToolState.NotConfigured,\r\n detail: inventory.instance.state,\r\n message: inventory.instance.message\r\n }),\r\n catchError((error) => {\r\n const message = Net.getErrorMessage(error);\r\n Logging.logError('ToolConditionValidator', `${id}: ${message}`);\r\n return of(<EnvironmentModuleToolConditionResult>{\r\n show: false,\r\n detail: EnvironmentModuleToolState.NotSupported,\r\n message\r\n });\r\n })\r\n );\r\n }\r\n\r\n private checkServerInventoryCondition(condition: EnvironmentModuleConditionMap, instance: ServerInventory): boolean {\r\n for (const property of ToolConditionValidator.serverInventoryProperties) {\r\n const conditionItem = condition[property.conditionName];\r\n if (conditionItem && !this.checkCondition(instance[property.dataName], conditionItem)) {\r\n return false;\r\n }\r\n }\r\n\r\n return true;\r\n }\r\n\r\n private checkCondition(data: any, condition: EnvironmentModuleConditionStatement): boolean {\r\n switch (condition.type) {\r\n case 'number':\r\n const numberValue: number = this.getNumberOrZero(data);\r\n const numberTest: number = this.getNumberOrZero(condition.value);\r\n switch (condition.operator) {\r\n case ToolConditionValidator.operators.gt:\r\n return numberValue > numberTest;\r\n case ToolConditionValidator.operators.ge:\r\n return numberValue >= numberTest;\r\n case ToolConditionValidator.operators.lt:\r\n return numberValue < numberTest;\r\n case ToolConditionValidator.operators.le:\r\n return numberValue <= numberTest;\r\n case ToolConditionValidator.operators.eq:\r\n return numberValue === numberTest;\r\n case ToolConditionValidator.operators.ne:\r\n return numberValue !== numberTest;\r\n case ToolConditionValidator.operators.is:\r\n return !!numberValue;\r\n case ToolConditionValidator.operators.not:\r\n return !numberValue;\r\n default:\r\n throw new Error(this.errorStrings.ToolValidationUnsupportedOperator.message.format(\r\n JSON.stringify(condition),\r\n JSON.stringify(data)));\r\n }\r\n\r\n case 'string':\r\n const stringValue: string = '' + data;\r\n const stringTest: string = '' + condition.value;\r\n switch (condition.operator) {\r\n case ToolConditionValidator.operators.gt:\r\n return stringValue.toLowerCase() > stringTest.toLowerCase();\r\n case ToolConditionValidator.operators.ge:\r\n return stringValue.toLowerCase() >= stringTest.toLowerCase();\r\n case ToolConditionValidator.operators.lt:\r\n return stringValue.toLowerCase() < stringTest.toLowerCase();\r\n case ToolConditionValidator.operators.le:\r\n return stringValue.toLowerCase() <= stringTest.toLowerCase();\r\n case ToolConditionValidator.operators.eq:\r\n return stringValue.toLowerCase() === stringTest.toLowerCase();\r\n case ToolConditionValidator.operators.ne:\r\n return stringValue.toLowerCase() !== stringTest.toLowerCase();\r\n case ToolConditionValidator.operators.is:\r\n return !!data;\r\n case ToolConditionValidator.operators.not:\r\n return !data;\r\n case ToolConditionValidator.operators.contains:\r\n return stringValue.toLowerCase().indexOf(stringTest.toLowerCase()) >= 0;\r\n case ToolConditionValidator.operators.notContains:\r\n return stringValue.toLowerCase().indexOf(stringTest.toLowerCase()) < 0;\r\n default:\r\n throw new Error(this.errorStrings.ToolValidationUnsupportedOperator.message.format(\r\n JSON.stringify(condition),\r\n JSON.stringify(data)));\r\n }\r\n\r\n case 'boolean':\r\n switch (condition.operator) {\r\n case ToolConditionValidator.operators.is:\r\n return !!data;\r\n case ToolConditionValidator.operators.not:\r\n return !data;\r\n default:\r\n throw new Error(this.errorStrings.ToolValidationUnsupportedOperator.message.format(\r\n JSON.stringify(condition),\r\n JSON.stringify(data)));\r\n }\r\n\r\n case 'version':\r\n const versionValue: string = data;\r\n const versionTest: string = <string>condition.value;\r\n return this.compareVersion(versionValue, versionTest, condition.operator);\r\n\r\n case 'numberArray':\r\n const checkNumber: any = condition.value;\r\n if (!checkNumber\r\n || typeof checkNumber === 'string'\r\n || !checkNumber.length\r\n || typeof checkNumber[0] !== 'number') {\r\n throw new Error(this.errorStrings.ToolValidationUnsupportedDataType.message.format(\r\n JSON.stringify(condition),\r\n JSON.stringify(data)));\r\n }\r\n\r\n const numberArrayValue: number = this.getNumberOrZero(data);\r\n const numberArray: number[] = <number[]>condition.value;\r\n for (const numberItem of numberArray) {\r\n const numberArrayTest = this.getNumberOrZero(numberItem);\r\n switch (condition.operator) {\r\n case ToolConditionValidator.operators.anyEq:\r\n if (numberArrayValue === numberArrayTest) {\r\n return true;\r\n }\r\n\r\n break;\r\n case ToolConditionValidator.operators.anyNe:\r\n if (numberArrayValue !== numberArrayTest) {\r\n return true;\r\n }\r\n\r\n break;\r\n default:\r\n throw new Error(this.errorStrings.ToolValidationUnsupportedOperator.message.format(\r\n JSON.stringify(condition),\r\n JSON.stringify(data)));\r\n }\r\n }\r\n\r\n return false;\r\n\r\n case 'stringArray':\r\n const checkString: any = condition.value;\r\n if (!checkString\r\n || typeof checkString === 'string'\r\n || !checkString.length\r\n || typeof checkString[0] !== 'string') {\r\n throw new Error(this.errorStrings.ToolValidationUnsupportedDataType.message.format(\r\n JSON.stringify(condition),\r\n JSON.stringify(data)));\r\n }\r\n\r\n const stringArrayValue: string = '' + data;\r\n const stringArray: string[] = <string[]>condition.value;\r\n for (const stringArrayTest of stringArray) {\r\n switch (condition.operator) {\r\n case ToolConditionValidator.operators.anyEq:\r\n if (stringArrayValue.toLowerCase() === stringArrayTest.toLowerCase()) {\r\n return true;\r\n }\r\n\r\n break;\r\n case ToolConditionValidator.operators.anyNe:\r\n if (stringArrayValue.toLowerCase() !== stringArrayTest.toLowerCase()) {\r\n return true;\r\n }\r\n\r\n break;\r\n case ToolConditionValidator.operators.anyContains:\r\n if (stringArrayValue.toLowerCase().indexOf(stringArrayTest.toLowerCase()) >= 0) {\r\n return true;\r\n }\r\n\r\n break;\r\n case ToolConditionValidator.operators.anyNotContains:\r\n if (stringArrayValue.toLowerCase().indexOf(stringArrayTest.toLowerCase()) < 0) {\r\n return true;\r\n }\r\n\r\n break;\r\n default:\r\n throw new Error(this.errorStrings.ToolValidationUnsupportedOperator.message.format(\r\n JSON.stringify(condition),\r\n JSON.stringify(data)));\r\n }\r\n }\r\n\r\n return false;\r\n\r\n default:\r\n throw new Error(this.errorStrings.ToolValidationUnsupportedDataType.message.format(\r\n JSON.stringify(condition),\r\n JSON.stringify(data)));\r\n }\r\n }\r\n\r\n private compareVersion(left: string, right: string, operator: string): boolean {\r\n const leftSegments = left.split('.');\r\n const rightSegments = right.split('.');\r\n if (!leftSegments || leftSegments.length <= 0 || !rightSegments || rightSegments.length <= 0) {\r\n throw new Error(this.errorStrings.ToolValidationVersionFormat.message);\r\n }\r\n\r\n const count = Math.max(leftSegments.length, rightSegments.length);\r\n let status;\r\n for (let index = 0; index < count; index++) {\r\n if (rightSegments[index] === '*') {\r\n // quit comparison with wildcard.\r\n status = 0;\r\n break;\r\n }\r\n\r\n const leftSegment = this.getNumberOrZero(leftSegments[index]);\r\n const rightSegment = this.getNumberOrZero(rightSegments[index]);\r\n if (leftSegment === rightSegment) {\r\n // equal.\r\n status = 0;\r\n } else if (leftSegment > rightSegment) {\r\n // greater.\r\n status = 1;\r\n break;\r\n } else {\r\n // lesser.\r\n status = -1;\r\n break;\r\n }\r\n }\r\n\r\n switch (operator) {\r\n case ToolConditionValidator.operators.gt:\r\n return status > 0;\r\n case ToolConditionValidator.operators.ge:\r\n return status >= 0;\r\n case ToolConditionValidator.operators.lt:\r\n return status < 0;\r\n case ToolConditionValidator.operators.le:\r\n return status <= 0;\r\n case ToolConditionValidator.operators.eq:\r\n return status === 0;\r\n case ToolConditionValidator.operators.ne:\r\n return status !== 0;\r\n default:\r\n throw new Error(\r\n this.errorStrings.ToolValidationUnsupportedOperator.message.format(operator, left));\r\n }\r\n }\r\n\r\n private getNumberOrZero(data: any): number {\r\n const result = Number(data);\r\n return isNaN(result) ? 0 : result;\r\n }\r\n}\r\n"]}