"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const _ = require("lodash");
const core_1 = require("@oclif/core");
const ops_1 = require("../../ops");
const contracts_1 = require("../../apis/codiac-relay/contracts");
const command_base_enterprise_1 = require("../../command-base-enterprise");
const cloud_provider_contextualizer_1 = require("../../uilogic/cloud-provider-contextualizer");
const read_stdin_1 = require("../../read-stdin");
const type_safety_utils_1 = require("../../uilogic/type-safety-utils");
class CspLogin extends command_base_enterprise_1.CommandBaseEnterprise {
    get uiCloudProvider() {
        if (this.__uiCloudProvider_backing === undefined) {
            this.__uiCloudProvider_backing = this.ioc.get(cloud_provider_contextualizer_1.CloudProviderContextualizer);
        }
        return this.__uiCloudProvider_backing;
    }
    bootstrap() {
        super.bootstrap();
        cloud_provider_contextualizer_1.CloudProviderContextualizer.bootstrap(this.ioc);
    }
    run() {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            return new Promise((resolve, reject) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                var _a;
                try {
                    this.bootstrap();
                    const { args, flags } = yield this.parse(CspLogin);
                    (_a = this._relayContainer) === null || _a === void 0 ? void 0 : _a.ready$.subscribe(() => tslib_1.__awaiter(this, void 0, void 0, function* () {
                        var _b, _c;
                        try {
                            if (flags.silent === true)
                                this.__ui = new ops_1.ThrowingUserInputProvider(this.logger);
                            this._userCntx = yield this.establishTenantContext(this._userCntx);
                            // Select the CSP
                            let provider;
                            if (args.provider) {
                                provider = type_safety_utils_1.TypeSafetyUtils.toEnum(contracts_1.CloudProviderEnum, args.provider);
                                if (provider == undefined)
                                    throw new Error("Unrecognized cloud provider.");
                            }
                            else if (flags.silent === true) {
                                throw new Error(`You must specify a cloud provider.`);
                            }
                            else {
                                // Select interactively
                                let allPvds = Object.values(contracts_1.CloudProviderEnum); //this.cspUiFactory.getAllClusterProviders(this.ioc);  // TODO: this call is currently limited to cluster providers - we need the comprehensive list of CSPs in use.
                                _.remove(allPvds, x => x == contracts_1.CloudProviderEnum.other);
                                _.remove(allPvds, x => x == contracts_1.CloudProviderEnum.localMachine);
                                provider = yield this.uiCloudProvider.promptForCloudProvider(allPvds, undefined);
                            }
                            let proprietaryInput;
                            // TODO: We should build this with better decoupled principles (eg: delegate to a ui provider instead of if-aws), but not worth it until there's more than just this one single outlier.
                            if (provider == contracts_1.CloudProviderEnum.aws && flags['aws-session-token'])
                                proprietaryInput = { sessionToken: flags['aws-session-token'] };
                            if (flags.clientSecret) {
                                // Client Credentials auth flow
                                let clientSecret = yield (0, read_stdin_1.default)();
                                if (_.isEmpty(clientSecret) && (flags.silent !== true)) {
                                    clientSecret = yield this._ui.promptPassword("clientSecret", "Client Secret: ");
                                }
                                if (_.isEmpty(clientSecret)) {
                                    throw new Error(`No client secret provided (client secret can be provided interatively or via the stdin process stream).`);
                                }
                                // BUILD REQUEST
                                let req = {
                                    providerCode: provider, credsType: 'client_credentials', username: "<not_relevant>", secret: clientSecret
                                };
                                if (flags.subscriptionId != undefined && !_.isEmpty(flags.subscriptionId)) {
                                    req.providerSubscription = flags.subscriptionId;
                                }
                                let result = yield this.genRelayClient.Call.csp_login(req);
                                if (result.success) {
                                    this._ui.userMessage(`${this._styler.colorGood("Success!")} Authenticated this Codiac instance as a trusted client with ${this._styler.hilite(provider)} for [${this._userCntx.currentTenantCode}: ${this._userCntx.userName}].`);
                                    this.logger.debug(`Authenticated this Codiac instance as a trusted client with ${this._styler.hilite(provider)} for [${this._userCntx.currentTenantCode}: ${this._userCntx.userName}].`);
                                }
                                else {
                                    this._ui.userMessage(`Authentication failed (${result.message})`);
                                }
                            }
                            else if (Boolean(flags.silent) === true) {
                                // Silent login - good luck...  (if user or password are empty, the api will pick up the stored creds for this csp)
                                let loginReq;
                                if (proprietaryInput) {
                                    loginReq = this.uiCloudProvider.assembleLoginRequest(this.ioc, provider, flags.user, flags.password, undefined, proprietaryInput);
                                }
                                else {
                                    loginReq = { providerCode: provider, username: flags.user, secret: flags.password };
                                }
                                const loginResult = yield this.genRelayClient.Call.csp_login(loginReq); // TODO: Consider passing in flags.subscriptionId
                                if (loginResult.success) {
                                    this._ui.userMessage(`${this._styler.colorGood("Success!")} Authenticated with ${this._styler.hilite(provider)} for [${this._userCntx.currentTenantCode}: ${this._userCntx.userName}].`);
                                    this.logger.debug(`Authenticated with ${this._styler.hilite(provider)} for [${this._userCntx.currentTenantCode}: ${this._userCntx.userName}].`);
                                }
                                else {
                                    this._ui.userMessage(`Authentication failed (${loginResult.message})`);
                                }
                            }
                            else {
                                // interactive auth flow
                                try {
                                    let cspUser = yield this.uiCloudProvider.promptForCloudProviderCreds(this.ioc, provider, Boolean(flags.silent), flags.user, flags.password, undefined, proprietaryInput);
                                    let subx = "";
                                    if (cspUser.subscriptionId) {
                                        subx = ((cspUser.provider == contracts_1.CloudProviderEnum.aws) ? "acct" : "subx").concat(": ", cspUser.subscriptionId);
                                        subx = "(".concat(subx, ")");
                                    }
                                    this._ui.userMessage(this._styler.colorGood("Success!").concat(" Authenticated with ", provider, " as ", this._styler.hilite(cspUser.username), subx));
                                }
                                catch (err) {
                                    this._ui.userMessage(`${this._styler.colorCritical("FAILED")} ${(_b = err.name) !== null && _b !== void 0 ? _b : ""} ${(_c = err.message) !== null && _c !== void 0 ? _c : ""}`);
                                }
                            }
                            resolve();
                        }
                        catch (err) {
                            // this.logger.error(err);
                            // this._ui.userMessage(`${this._styler.colorCritical("ERROR")} ${err.name ?? ""} ${err.message ?? ""}`);
                            this.handleError(err);
                            resolve();
                        }
                        finally {
                            yield this.closeConnections();
                        }
                    }));
                }
                catch (err) {
                    this.handleError(err);
                    reject(err);
                }
                finally {
                    yield this.closeConnections();
                }
            }));
        });
    }
}
exports.default = CspLogin;
CspLogin.description = 'Authenticates with the given Cloud Service Provider (CSP), eg: aws, azure, docker.  Use this command both to validate credentials and to declare the csp user account for subsequent commands involving that provider.';
CspLogin.examples = [
    'cod csp login',
    'cod csp login docker',
    'cat mypassword.txt | cod csp login -u me@mydomain.com --silent docker',
    'cat theclientsecret.txt | cod csp login --clientSecret --silent azure',
    'cod csp login -u ASIAZ3XYZPDQ123 -p $(cat my-aws-secret-key.txt) aws',
    'cod csp login -u ASIAZ3XYZPDQ123 -p $(cat my-aws-secret-key.txt) --aws-session-token $(cat my-aws-sesh.txt) aws'
];
// static dependsOn = ["provider"];  // this isnt the whole of the requirements
CspLogin.flags = {
    user: core_1.Flags.string({ char: 'u', description: 'The username at the given cloud provider.', exclusive: ["clientSecret"] }),
    password: core_1.Flags.string({ char: 'p', description: '(must be provided via stdin) The password at the given cloud provider.', exclusive: ["clientSecret"] }),
    "aws-session-token": core_1.Flags.string({ required: false, exclusive: ["clientSecret"], description: "(for AWS only) The session token parameter for use when authenticating with a short-term AWS access key credentials." }),
    clientSecret: core_1.Flags.boolean({ exclusive: ["user", "password"], description: '(must be provided interactively or via stdin) Invokes the OAuth Client Credentials Flow. Used to authenticate when no user is present, such as an internal service or in a devops pipeline.  Note: This is a secret that must be created when Codiac gets registered as a trusted application in your cloud service provider subscription (eg: in azure, this is an app registration in Azure AD).' }),
    subscriptionId: core_1.Flags.string({ char: 's', description: '(Required in silent mode) The Id of the cloud provider subscription under which to authenticate.' }),
    silent: core_1.Flags.boolean({ char: 'y', required: false, description: `(Optional: defaults to false) Suppresses any user-interaction.` })
};
CspLogin.args = {
    provider: core_1.Args.string({ required: false, options: Object.keys(contracts_1.CloudProviderEnum), description: 'Code indicating the Cloud Service Provider (CSP) being authenticated with.' })
};
//# sourceMappingURL=login.js.map