"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.findLatestStable = exports.extractDigestFromResponseBody = exports.getRegistryRepository = exports.getAuthHeaders = exports.isDockerHost = exports.DOCKER_HUB = exports.gitRefLabel = exports.sourceLabels = exports.dockerDatasourceId = void 0;
const tslib_1 = require("tslib");
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const auth_header_1 = require("auth-header");
const error_messages_1 = require("../../../constants/error-messages");
const logger_1 = require("../../../logger");
const external_host_error_1 = require("../../../types/errors/external-host-error");
const hash_1 = require("../../../util/hash");
const hostRules = tslib_1.__importStar(require("../../../util/host-rules"));
const regex_1 = require("../../../util/regex");
const sanitize_1 = require("../../../util/sanitize");
const url_1 = require("../../../util/url");
const docker_1 = require("../../versioning/docker");
const ecr_1 = require("./ecr");
exports.dockerDatasourceId = 'docker';
exports.sourceLabels = [
    'org.opencontainers.image.source',
    'org.label-schema.vcs-url',
];
exports.gitRefLabel = 'org.opencontainers.image.revision';
exports.DOCKER_HUB = 'https://index.docker.io';
function isDockerHost(host) {
    const regex = (0, regex_1.regEx)(/(?:^|\.)docker\.io$/);
    return regex.test(host);
}
exports.isDockerHost = isDockerHost;
async function getAuthHeaders(http, registryHost, dockerRepository, apiCheckUrl = `${registryHost}/v2/`) {
    try {
        const options = {
            throwHttpErrors: false,
            noAuth: true,
        };
        const apiCheckResponse = apiCheckUrl.endsWith('/v2/')
            ? await http.get(apiCheckUrl, options)
            : // use json request, as this will be cached for tags, so it returns json
                // TODO: add cache test
                await http.getJson(apiCheckUrl, options);
        if (apiCheckResponse.statusCode === 200) {
            logger_1.logger.debug(`No registry auth required for ${apiCheckUrl}`);
            return {};
        }
        if (apiCheckResponse.statusCode === 404) {
            logger_1.logger.debug(`Page Not Found ${apiCheckUrl}`);
            // throw error up to be caught and potentially retried with library/ prefix
            throw new Error(error_messages_1.PAGE_NOT_FOUND_ERROR);
        }
        if (apiCheckResponse.statusCode !== 401 ||
            !is_1.default.nonEmptyString(apiCheckResponse.headers['www-authenticate'])) {
            logger_1.logger.warn({ apiCheckUrl, res: apiCheckResponse }, 'Invalid registry response');
            return null;
        }
        const authenticateHeader = (0, auth_header_1.parse)(apiCheckResponse.headers['www-authenticate']);
        const opts = hostRules.find({
            hostType: exports.dockerDatasourceId,
            url: apiCheckUrl,
        });
        if (ecr_1.ecrRegex.test(registryHost)) {
            logger_1.logger.trace({ registryHost, dockerRepository }, `Using ecr auth for Docker registry`);
            const [, region] = ecr_1.ecrRegex.exec(registryHost) ?? [];
            const auth = await (0, ecr_1.getECRAuthToken)(region, opts);
            if (auth) {
                opts.headers = { authorization: `Basic ${auth}` };
            }
        }
        else if (opts.username && opts.password) {
            logger_1.logger.trace({ registryHost, dockerRepository }, `Using basic auth for Docker registry`);
            const auth = Buffer.from(`${opts.username}:${opts.password}`).toString('base64');
            opts.headers = { authorization: `Basic ${auth}` };
        }
        else if (opts.token) {
            const authType = opts.authType ?? 'Bearer';
            logger_1.logger.trace({ registryHost, dockerRepository }, `Using ${authType} token for Docker registry`);
            opts.headers = { authorization: `${authType} ${opts.token}` };
        }
        delete opts.username;
        delete opts.password;
        delete opts.token;
        // If realm isn't an url, we should directly use auth header
        // Can happen when we get a Basic auth or some other auth type
        // * WWW-Authenticate: Basic realm="Artifactory Realm"
        // * Www-Authenticate: Basic realm="https://123456789.dkr.ecr.eu-central-1.amazonaws.com/",service="ecr.amazonaws.com"
        // * www-authenticate: Bearer realm="https://ghcr.io/token",service="ghcr.io",scope="repository:user/image:pull"
        // * www-authenticate: Bearer realm="https://auth.docker.io/token",service="registry.docker.io"
        if (authenticateHeader.scheme.toUpperCase() !== 'BEARER' ||
            !is_1.default.string(authenticateHeader.params.realm) ||
            (0, url_1.parseUrl)(authenticateHeader.params.realm) === null) {
            logger_1.logger.trace({ registryHost, dockerRepository, authenticateHeader }, `Invalid realm, testing direct auth`);
            return opts.headers ?? null;
        }
        const authUrl = new URL(`${authenticateHeader.params.realm}`);
        // repo isn't known to server yet, so causing wrong scope `repository:user/image:pull`
        if (is_1.default.string(authenticateHeader.params.scope) &&
            !apiCheckUrl.endsWith('/v2/')) {
            authUrl.searchParams.append('scope', authenticateHeader.params.scope);
        }
        else {
            authUrl.searchParams.append('scope', `repository:${dockerRepository}:pull`);
        }
        if (is_1.default.string(authenticateHeader.params.service)) {
            authUrl.searchParams.append('service', authenticateHeader.params.service);
        }
        logger_1.logger.trace({ registryHost, dockerRepository, authUrl: authUrl.href }, `Obtaining docker registry token`);
        opts.noAuth = true;
        const authResponse = (await http.getJson(authUrl.href, opts)).body;
        const token = authResponse.token ?? authResponse.access_token;
        // istanbul ignore if
        if (!token) {
            logger_1.logger.warn('Failed to obtain docker registry token');
            return null;
        }
        // sanitize token
        (0, sanitize_1.addSecretForSanitizing)(token);
        return {
            authorization: `Bearer ${token}`,
        };
    }
    catch (err) /* istanbul ignore next */ {
        if (err.host === 'quay.io') {
            // TODO: debug why quay throws errors (#9604)
            return null;
        }
        if (err.statusCode === 401) {
            logger_1.logger.debug({ registryHost, dockerRepository }, 'Unauthorized docker lookup');
            logger_1.logger.debug({ err });
            return null;
        }
        if (err.statusCode === 403) {
            logger_1.logger.debug({ registryHost, dockerRepository }, 'Not allowed to access docker registry');
            logger_1.logger.debug({ err });
            return null;
        }
        if (err.name === 'RequestError' && isDockerHost(registryHost)) {
            throw new external_host_error_1.ExternalHostError(err);
        }
        if (err.statusCode === 429 && isDockerHost(registryHost)) {
            throw new external_host_error_1.ExternalHostError(err);
        }
        if (err.statusCode >= 500 && err.statusCode < 600) {
            throw new external_host_error_1.ExternalHostError(err);
        }
        if (err.message === error_messages_1.PAGE_NOT_FOUND_ERROR) {
            throw err;
        }
        if (err.message === error_messages_1.HOST_DISABLED) {
            logger_1.logger.trace({ registryHost, dockerRepository, err }, 'Host disabled');
            return null;
        }
        logger_1.logger.warn({ registryHost, dockerRepository, err }, 'Error obtaining docker token');
        return null;
    }
}
exports.getAuthHeaders = getAuthHeaders;
function getRegistryRepository(packageName, registryUrl) {
    if (registryUrl !== exports.DOCKER_HUB) {
        const registryEndingWithSlash = (0, url_1.ensureTrailingSlash)(registryUrl.replace((0, regex_1.regEx)(/^https?:\/\//), ''));
        if (packageName.startsWith(registryEndingWithSlash)) {
            let registryHost = (0, url_1.trimTrailingSlash)(registryUrl);
            if (!(0, regex_1.regEx)(/^https?:\/\//).test(registryHost)) {
                registryHost = `https://${registryHost}`;
            }
            let dockerRepository = packageName.replace(registryEndingWithSlash, '');
            const fullUrl = `${registryHost}/${dockerRepository}`;
            const { origin, pathname } = (0, url_1.parseUrl)(fullUrl);
            registryHost = origin;
            dockerRepository = pathname.substring(1);
            return {
                registryHost,
                dockerRepository,
            };
        }
    }
    let registryHost;
    const split = packageName.split('/');
    if (split.length > 1 && (split[0].includes('.') || split[0].includes(':'))) {
        [registryHost] = split;
        split.shift();
    }
    let dockerRepository = split.join('/');
    if (!registryHost) {
        registryHost = registryUrl.replace('https://docker.io', 'https://index.docker.io');
    }
    if (registryHost === 'docker.io') {
        registryHost = 'index.docker.io';
    }
    if (!(0, regex_1.regEx)(/^https?:\/\//).exec(registryHost)) {
        registryHost = `https://${registryHost}`;
    }
    const opts = hostRules.find({
        hostType: exports.dockerDatasourceId,
        url: registryHost,
    });
    if (opts?.insecureRegistry) {
        registryHost = registryHost.replace('https', 'http');
    }
    if (registryHost.endsWith('.docker.io') && !dockerRepository.includes('/')) {
        dockerRepository = 'library/' + dockerRepository;
    }
    return {
        registryHost,
        dockerRepository,
    };
}
exports.getRegistryRepository = getRegistryRepository;
function extractDigestFromResponseBody(manifestResponse) {
    return 'sha256:' + (0, hash_1.toSha256)(manifestResponse.body);
}
exports.extractDigestFromResponseBody = extractDigestFromResponseBody;
function findLatestStable(tags) {
    let stable = null;
    for (const tag of tags) {
        if (!docker_1.api.isValid(tag) || !docker_1.api.isStable(tag)) {
            continue;
        }
        if (!stable || docker_1.api.isGreaterThan(tag, stable)) {
            stable = tag;
        }
    }
    return stable;
}
exports.findLatestStable = findLatestStable;
//# sourceMappingURL=common.js.map