{"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"]}