{"version":3,"sources":["../../../packages/core/data/asset-manager.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAE7D;;GAEG;AACH,qBAAa,YAAY;IAiBT,OAAO,CAAC,mBAAmB;IAA6B,OAAO,CAAC,MAAM,CAAC;IAAW,OAAO,CAAC,eAAe,CAAC;IAhBtH;;OAEG;IACH,IAAW,KAAK,YAEf;IAED;;OAEG;IACH,IAAW,cAAc,YAExB;IAED,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAW;gBAEpB,mBAAmB,EAAE,mBAAmB,CAAC,IAAI,CAAC,EAAU,MAAM,CAAC,EAAE,OAAO,EAAU,eAAe,CAAC,EAAE,OAAO;IAExH,QAAQ,CAAC,KAAK,EAAE,MAAM;IAQ7B;;;;;OAKG;IACI,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,aAAa,GAAG,IAAI;IAsFrE,SAAS,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI;IAyBjD;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;CAe5B","file":"asset-manager.d.ts","sourcesContent":["import { LogLevel } from '../diagnostics/log-level';\r\nimport { Logging } from '../diagnostics/logging';\r\nimport { LocalizationManager } from './localization-manager';\r\n\r\n/**\r\n * Class for asset manager\r\n */\r\nexport class AssetManager {\r\n    /**\r\n     * Getter for cssV2.\r\n     */\r\n    public get cssV2() {\r\n        return !!this._cssV2;\r\n    }\r\n\r\n    /**\r\n     * Getter for if the target is React.\r\n     */\r\n    public get reactExtension() {\r\n        return !!this._reactExtension;\r\n    }\r\n\r\n    private readonly fontsKeyword = 'fonts';\r\n\r\n    constructor(private localizationManager: LocalizationManager<void>, private _cssV2?: boolean, private _reactExtension?: boolean) { }\r\n\r\n    public setTheme(theme: string) {\r\n        const self = MsftSme.self();\r\n        const oldTheme = self.Resources.theme;\r\n        self.Resources.theme = theme;\r\n        // apply the theme class to the body of the document\r\n        document.body.classList.remove(`sme-theme-${oldTheme}`);\r\n        document.body.classList.add(`sme-theme-${theme}`);\r\n    }\r\n    /**\r\n     * Injects dynamic assets (css, js, etc..) from the shell\r\n     * This is only meant to be called once during an extensions lifecycle (during init)\r\n     * @param theme the current theme name\r\n     * @param assets the assets to process\r\n     */\r\n    public loadAssets(theme: string, assets: MsftSme.MsftSmeAssets): void {\r\n        const self = MsftSme.self();\r\n        self.Resources.assets = assets;\r\n\r\n        this.setTheme(theme);\r\n\r\n        if (!assets) {\r\n            return;\r\n        }\r\n\r\n        // Do not load css if it is a react extension.\r\n        if (this.reactExtension) {\r\n            Logging.log({\r\n                level: LogLevel.Informational,\r\n                message: 'React extension detected. Skip loading css from shell.',\r\n                source: 'AssetManager.loadAssets'\r\n            });\r\n            return;\r\n        }\r\n\r\n        // inject css tags into header\r\n        let cssAssets = assets.css;\r\n        if (this._cssV2) {\r\n            if (assets.cssV2) {\r\n                cssAssets = assets.cssV2;\r\n                Logging.log({\r\n                    level: LogLevel.Informational,\r\n                    message: 'Using cssV2',\r\n                    source: 'AssetManager.loadAssets'\r\n                });\r\n            } else if (assets.css) {\r\n                // Backup solution for scenario where customer using GA release shell before publishing the cssV2 feature with\r\n                // repository that has been using the cssV2.\r\n                // In this case, we only allow fonts override to happen.\r\n                cssAssets = this.updateCssV2Assets(assets.css);\r\n\r\n                Logging.log({\r\n                    level: LogLevel.Warning,\r\n                    message: 'Empty assets warning. Using older assets for fonts. It is suggested to update the WAC to the latest(2211 release) GA build.',\r\n                    source: 'AssetManager.loadAssets'\r\n                });\r\n            } else {\r\n                Logging.log({\r\n                    level: LogLevel.Critical,\r\n                    message: 'Empty assets. Try disable cache and refresh the browser or re-install the latest WAC build',\r\n                    source: 'AssetManager.loadAssets'\r\n                });\r\n            }\r\n        }\r\n\r\n        this.appendAssets(cssAssets);\r\n\r\n        /**\r\n         * The js injection mechanism below is subject to the following attack:\r\n         *\r\n         * 1. User visits malicious website (MW) from their workstation\r\n         * 2. MW randomly or sequentially opens hidden iframes to localhost on various ports.\r\n         * 3. once each iframe loads it send rpc init and impersonates the shell side of the communication channel\r\n         * 4. The iframe will respond because it trusts * domains for onMessage requests.(this is a basic requirement of our infrastructure)\r\n         * 5. The MW can then inject any javascript it wants into the module and presumably knows the gateway is running on the same port.\r\n         * 6. Because we use windows authentication, the MW can execute powershell requests on any servers the user has access to.\r\n         * 7. The MW has now compromised the server acting as the user.\r\n         *\r\n         * How to fix:\r\n         * In order for this to work, we need an ironclad way of validating that our parent is the shell.\r\n         * some possibilities are:\r\n         *\r\n         * 1. Three way handshake with gateway to discover the only acceptable shell origin.\r\n         *      a. this could be done with javascript or it could be a static file that the module always reads at startup\r\n         * 2. certificate based authentication before rpc communication\r\n         * 3. other methods?\r\n         *\r\n         * Disabling until we have a more solid use case and we know the most secure way to achieve this functionality.\r\n         */\r\n\r\n        // // inject js tags into header\r\n        // if (assets.js) {\r\n        //     assets.js.forEach(href => {\r\n        //         let script = document.createElement('script');\r\n        //         script.setAttribute('type', 'text/javascript');\r\n        //         script.setAttribute('src', href);\r\n        //         head.appendChild(script);\r\n        //     });\r\n        // }\r\n    }\r\n\r\n    protected appendAssets(cssAssets: string[]): void {\r\n        if (!cssAssets) {\r\n            return;\r\n        }\r\n\r\n        // get the page header\r\n        const head = document.getElementsByTagName('head')[0];\r\n\r\n        cssAssets.forEach(href => {\r\n            const link: HTMLLinkElement = document.createElement('link');\r\n            link.rel = 'stylesheet';\r\n            link.type = 'text/css';\r\n            link.crossOrigin = 'cors';\r\n            link.href = href;\r\n\r\n            Logging.log({\r\n                level: LogLevel.Informational,\r\n                message: link,\r\n                source: 'AssetManager.loadAssets'\r\n            });\r\n\r\n            head.appendChild(link);\r\n        });\r\n    }\r\n\r\n    /**\r\n     * Only push the fonts and let the extension use cssV2.\r\n     * @param cssAssets the input cssV1 assets.\r\n     * @returns the cssAssetsV2 including fonts.css only.\r\n     */\r\n    private updateCssV2Assets(cssAssets: string[]): string[] {\r\n        const cssAssetsV2 = [];\r\n\r\n        if (!cssAssets) {\r\n            return cssAssetsV2;\r\n        }\r\n\r\n        for (let i = 0; i < cssAssets.length; i++) {\r\n            if (cssAssets[i] && cssAssets[i].includes(this.fontsKeyword)) {\r\n                cssAssetsV2.push(cssAssets[i]);\r\n            }\r\n        }\r\n\r\n        return cssAssetsV2;\r\n    }\r\n}\r\n"]}