"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SingleSignOn = void 0;
const electron_1 = require("electron");
const crypto = __importStar(require("crypto"));
const path = __importStar(require("path"));
const url_1 = require("url");
const commons_1 = require("@wireapp/commons");
const ElectronUtil_1 = require("../lib/ElectronUtil");
const getLogger_1 = require("../logging/getLogger");
const lifecycle_1 = require("../runtime/lifecycle");
const config_1 = require("../settings/config");
const WindowUtil = __importStar(require("../window/WindowUtil"));
const minimist = require('minimist');
const argv = minimist(process.argv.slice(1));
const LOG_DIR = path.join(electron_1.app.getPath('userData'), 'logs');
class SingleSignOn {
    constructor(ssoWindow, senderEvent, windowOriginURL, windowOptions) {
        this.onClose = () => { };
        this.init = () => __awaiter(this, void 0, void 0, function* () {
            var _a, _b;
            this.session = electron_1.session.fromPartition(SingleSignOn.SSO_SESSION_NAME, { cache: false });
            this.session.setPermissionRequestHandler((_webContents, _permission, callback) => callback(false));
            this.session.webRequest.onBeforeSendHeaders(({ requestHeaders }, callback) => {
                requestHeaders['User-Agent'] = config_1.config.userAgent;
                callback({ cancel: false, requestHeaders });
            });
            this.setupBrowserWindow();
            yield SingleSignOn.registerProtocol(this.session, type => this.finalizeLogin(type));
            yield ((_a = this.ssoWindow) === null || _a === void 0 ? void 0 : _a.loadURL(this.windowOriginUrl.toString()));
            if (typeof argv[config_1.config.ARGUMENT.DEVTOOLS] !== 'undefined') {
                (_b = this.ssoWindow) === null || _b === void 0 ? void 0 : _b.webContents.openDevTools({ mode: 'detach' });
            }
            return this;
        });
        this.close = () => {
            (() => __awaiter(this, void 0, void 0, function* () {
                var _a;
                if (this.session) {
                    yield this.wipeSessionData();
                    const unregisterSuccess = SingleSignOn.unregisterProtocol(this.session);
                    if (!unregisterSuccess) {
                        console.error('Failed to unregister protocol');
                    }
                }
                (_a = this.ssoWindow) === null || _a === void 0 ? void 0 : _a.close();
                this.session = undefined;
                this.ssoWindow = undefined;
            }))()
                .then(console.info)
                .catch(console.info);
        };
        this.focus = () => {
            var _a;
            (_a = this.ssoWindow) === null || _a === void 0 ? void 0 : _a.focus();
        };
        this.finalizeLogin = (type) => __awaiter(this, void 0, void 0, function* () {
            if (type === SingleSignOn.RESPONSE_TYPES.AUTH_SUCCESS) {
                if (!this.session) {
                    yield this.dispatchResponse(SingleSignOn.RESPONSE_TYPES.AUTH_ERROR_SESS_NOT_AVAILABLE);
                    return;
                }
                try {
                    yield SingleSignOn.copyCookies(this.session, this.senderWebContents.session, this.windowOriginUrl);
                }
                catch (error) {
                    SingleSignOn.logger.warn(error);
                    yield this.dispatchResponse(SingleSignOn.RESPONSE_TYPES.AUTH_ERROR_COOKIE);
                    return;
                }
            }
            yield this.dispatchResponse(type);
        });
        this.windowOptions = windowOptions;
        this.ssoWindow = ssoWindow;
        this.senderWebContents = senderEvent.sender;
        this.windowOriginUrl = new url_1.URL(windowOriginURL);
    }
    setupBrowserWindow() {
        if (!this.ssoWindow) {
            throw new Error('ssoWindow is not defined');
        }
        const ssoWindow = this.ssoWindow;
        if (this.windowOptions.webPreferences) {
            delete this.windowOptions.webPreferences.preload;
        }
        ssoWindow.once('closed', () => __awaiter(this, void 0, void 0, function* () {
            if (this.session) {
                yield this.wipeSessionData();
                const unregisterSuccess = SingleSignOn.unregisterProtocol(this.session);
                if (!unregisterSuccess) {
                    throw new Error('Failed to unregister protocol');
                }
            }
            this.onClose();
            this.session = undefined;
            this.ssoWindow = undefined;
        }));
        ssoWindow.on('page-title-updated', event => event.preventDefault());
        ssoWindow.webContents.setWindowOpenHandler((details) => {
            void WindowUtil.openExternal(details.url, true);
            return { action: 'deny' };
        });
        ssoWindow.webContents.on('will-navigate', (event, url) => {
            const { origin } = new url_1.URL(url);
            if (origin.length > SingleSignOn.MAX_LENGTH_ORIGIN) {
                event.preventDefault();
            }
            ssoWindow.setTitle(SingleSignOn.getWindowTitle(origin));
        });
        if (getLogger_1.ENABLE_LOGGING) {
            ssoWindow.webContents.on('console-message', (_event, _level, message) => __awaiter(this, void 0, void 0, function* () {
                const webViewId = (0, lifecycle_1.getWebViewId)(ssoWindow.webContents);
                if (webViewId) {
                    const logFilePath = path.join(LOG_DIR, webViewId, config_1.config.logFileName);
                    try {
                        yield commons_1.LogFactory.writeMessage(message, logFilePath);
                    }
                    catch (error) {
                        console.error(`Cannot write to log file "${logFilePath}": ${error.message}`, error);
                    }
                }
            }));
        }
    }
    static copyCookies(fromSession, toSession, url) {
        return __awaiter(this, void 0, void 0, function* () {
            const cookies = yield fromSession.cookies.get({ name: 'zuid' });
            for (const cookie of cookies) {
                if (cookie.domain) {
                    yield toSession.cookies.set(Object.assign({ url: url.toString() }, cookie));
                }
            }
            yield toSession.cookies.flushStore();
        });
    }
    static generateSecret(length) {
        return new Promise((resolve, reject) => {
            crypto.randomBytes(length, (error, bytes) => (error ? reject(error) : resolve(bytes.toString('hex'))));
        });
    }
    static registerProtocol(session, finalizeLogin) {
        return __awaiter(this, void 0, void 0, function* () {
            SingleSignOn.loginAuthorizationSecret = yield SingleSignOn.generateSecret(24);
            const handleRequest = (request) => {
                try {
                    const requestURL = new url_1.URL(request.url);
                    if (requestURL.protocol !== `${SingleSignOn.SSO_PROTOCOL}:`) {
                        throw new Error('Protocol is invalid');
                    }
                    if (requestURL.hostname !== SingleSignOn.SSO_PROTOCOL_HOST) {
                        throw new Error('Host is invalid');
                    }
                    if (typeof SingleSignOn.loginAuthorizationSecret !== 'string') {
                        throw new Error('Secret has not be set or has been consumed');
                    }
                    if (requestURL.searchParams.get('secret') !== SingleSignOn.loginAuthorizationSecret) {
                        throw new Error('Secret is invalid');
                    }
                    const type = requestURL.searchParams.get('type');
                    if (typeof type !== 'string') {
                        throw new Error('Response is empty');
                    }
                    if (type.length > SingleSignOn.SSO_PROTOCOL_RESPONSE_SIZE_LIMIT) {
                        throw new Error('Response type is too long');
                    }
                    finalizeLogin(type);
                }
                catch (error) {
                    SingleSignOn.logger.error(error);
                }
            };
            const isRegistered = session.protocol.isProtocolRegistered(SingleSignOn.SSO_PROTOCOL);
            if (!isRegistered) {
                const registerSuccess = session.protocol.registerStringProtocol(SingleSignOn.SSO_PROTOCOL, handleRequest);
                if (!registerSuccess) {
                    throw new Error('Failed to register protocol.');
                }
            }
        });
    }
    static unregisterProtocol(session) {
        return session.protocol.unregisterProtocol(SingleSignOn.SSO_PROTOCOL);
    }
    dispatchResponse(type) {
        return __awaiter(this, void 0, void 0, function* () {
            const isTypeValid = /^[A-Z_]{1,255}$/g;
            if (isTypeValid.test(type) === false) {
                throw new Error('Invalid type detected, aborting.');
            }
            const snippet = `window.dispatchEvent(new MessageEvent('message', {origin: '${this.windowOriginUrl.origin}', data: {type: '${type}'}}))`;
            yield (0, ElectronUtil_1.executeJavaScriptWithoutResult)(snippet, this.senderWebContents);
        });
    }
    wipeSessionData() {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            yield ((_a = this.session) === null || _a === void 0 ? void 0 : _a.clearStorageData(undefined));
        });
    }
}
exports.SingleSignOn = SingleSignOn;
SingleSignOn.ALLOWED_BACKEND_ORIGINS = config_1.config.backendOrigins;
SingleSignOn.SINGLE_SIGN_ON_FRAME_NAME = 'WIRE_SSO';
SingleSignOn.SSO_PROTOCOL = `${config_1.config.customProtocolName}-sso`;
SingleSignOn.SSO_PROTOCOL_HOST = 'response';
SingleSignOn.SSO_PROTOCOL_RESPONSE_SIZE_LIMIT = 255;
SingleSignOn.SSO_SESSION_NAME = 'sso';
SingleSignOn.MAX_LENGTH_ORIGIN_DOMAIN = 255;
SingleSignOn.MAX_LENGTH_ORIGIN = 'https://'.length + SingleSignOn.MAX_LENGTH_ORIGIN_DOMAIN;
SingleSignOn.logger = (0, getLogger_1.getLogger)(path.basename(__filename));
SingleSignOn.RESPONSE_TYPES = {
    AUTH_ERROR_COOKIE: 'AUTH_ERROR_COOKIE',
    AUTH_ERROR_SESS_NOT_AVAILABLE: 'AUTH_ERROR_SESS_NOT_AVAILABLE',
    AUTH_SUCCESS: 'AUTH_SUCCESS',
};
SingleSignOn.isSingleSignOnLoginWindow = (frameName) => SingleSignOn.SINGLE_SIGN_ON_FRAME_NAME === frameName;
SingleSignOn.getWindowTitle = (origin) => SingleSignOn.ALLOWED_BACKEND_ORIGINS.includes(origin) ? '' : origin;
//# sourceMappingURL=SingleSignOn.js.map