"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getIpIsNotNetworkOrBroadcastAddressSchema = exports.getIpAddressInSubnetValidationSchema = exports.isNotReservedHostDNSAddress = exports.isNotReservedHostIPAddress = exports.isReservedIpv6Address = exports.getMultipleIpAddressValidationSchema = exports.getIpAddressValidationSchema = exports.getUniqueValidationSchema = void 0;
const tslib_1 = require("tslib");
const ip_address_1 = require("ip-address");
const is_in_subnet_1 = require("is-in-subnet");
const Yup = tslib_1.__importStar(require("yup"));
const protocolVersion_1 = require("./data/protocolVersion");
const dataTypes_1 = require("./data/dataTypes");
const common_1 = require("../../../../common");
const RESERVED_IPS = ['127.0.0.0', '127.0.0.1', '0.0.0.0', '255.255.255.255'];
const getUniqueValidationSchema = (uniqueStringArrayExtractor) => {
    return Yup.string().test('unique', 'Value must be unique', (value, testContext) => {
        const context = testContext.options.context;
        if (!context || !context.values) {
            return testContext.createError({
                message: 'Unexpected error: Yup test context should contain form values',
            });
        }
        const values = uniqueStringArrayExtractor(context.values, testContext, value);
        if (!values) {
            return testContext.createError({
                message: 'Unexpected error: Failed to get values to test uniqueness',
            });
        }
        return (values.filter((currentValue) => currentValue.toLowerCase() === (value === null || value === void 0 ? void 0 : value.toLowerCase()))
            .length === 1);
    });
};
exports.getUniqueValidationSchema = getUniqueValidationSchema;
const isValidIPv4Address = (addressStr) => {
    try {
        // ip-address package treats cidr addresses as valid so need to verify it isn't a cidr
        // Can't use Address4.isValid()
        const address = new ip_address_1.Address4(addressStr);
        return !address.parsedSubnet;
    }
    catch (e) {
        return false;
    }
};
const isValidIPv6Address = (addressStr) => {
    try {
        // ip-address package treats cidr addresses as valid so need to verify it isn't a cidr
        // Can't use Address6.isValid()
        const address = new ip_address_1.Address6(addressStr);
        return !address.parsedSubnet;
    }
    catch (e) {
        return false;
    }
};
const isValidAddress = (addressStr, protocolVersion) => {
    if (protocolVersion === undefined) {
        return isValidIPv4Address(addressStr) || isValidIPv6Address(addressStr);
    }
    return protocolVersion === dataTypes_1.ProtocolVersion.ipv4
        ? isValidIPv4Address(addressStr)
        : isValidIPv6Address(addressStr);
};
const isReservedAddress = (addressStr, protocolVersion) => {
    try {
        if ((protocolVersion === undefined && isValidIPv4Address(addressStr)) ||
            protocolVersion === dataTypes_1.ProtocolVersion.ipv4) {
            return RESERVED_IPS.includes(addressStr);
        }
        else {
            return (0, exports.isReservedIpv6Address)(new ip_address_1.Address6(addressStr));
        }
    }
    catch (e) {
        return false;
    }
};
const getIpAddressValidationSchema = (protocolVersion) => {
    const protocolVersionLabel = protocolVersion === dataTypes_1.ProtocolVersion.ipv4 ? 'IPv4' : 'IPv6';
    return Yup.string().test(protocolVersion, `Value \${value} is not a valid ${protocolVersionLabel} address`, (value) => {
        if (!value) {
            return true;
        }
        return isValidAddress(value, protocolVersion);
    });
};
exports.getIpAddressValidationSchema = getIpAddressValidationSchema;
const getMultipleIpAddressValidationSchema = (protocolVersion) => {
    const validationId = protocolVersion === undefined ? 'is-ipv4-or-ipv6-csv' : protocolVersion;
    const protocolVersionLabel = protocolVersion === undefined
        ? 'IPv4 or IPv6'
        : protocolVersion === dataTypes_1.ProtocolVersion.ipv4
            ? 'IPv4'
            : 'IPv6';
    return Yup.string().test(validationId, ({ value }) => {
        const addresses = value.split(',');
        const invalidAddresses = addresses.filter((address) => !isValidAddress(address, protocolVersion));
        const displayValue = invalidAddresses.join(', ');
        if (invalidAddresses.length === 1) {
            return `Value ${displayValue} is not a valid ${protocolVersionLabel} address`;
        }
        else if (invalidAddresses.length > 1) {
            return `The values ${displayValue} are not valid ${protocolVersionLabel} addresses`;
        }
        // If all addresses are valid, then there must be duplicated addresses
        const duplicates = (0, common_1.getDuplicates)(addresses);
        return `The following IP addresses are duplicated: ${duplicates.join(',')}`;
    }, (value) => {
        if (!value) {
            return true;
        }
        const addresses = value.split(',');
        const duplicates = (0, common_1.getDuplicates)(addresses);
        if (duplicates.length !== 0) {
            return false;
        }
        return addresses.every((address) => isValidAddress(address, protocolVersion));
    });
};
exports.getMultipleIpAddressValidationSchema = getMultipleIpAddressValidationSchema;
const isReservedIpv6Address = (ipv6Address) => {
    return ipv6Address.isLoopback() || ipv6Address.isMulticast();
};
exports.isReservedIpv6Address = isReservedIpv6Address;
function areNotReservedAdresses(value, protocolVersion) {
    if (!value) {
        return true;
    }
    // The field may admit multiple values as a comma-separated string
    const addresses = value.split(',');
    return addresses.every((address) => !isReservedAddress(address, protocolVersion));
}
const isNotReservedHostIPAddress = (protocolVersion) => {
    return Yup.string().test('is-not-reserved-ip-address', ({ value }) => {
        const addresses = value.split(',');
        if (addresses.length === 1) {
            return `The provided IP address is not a correct address for an interface.`;
        }
        const reservedAddresses = addresses.filter((address) => {
            return isReservedAddress(address, protocolVersion);
        });
        return `The provided IP addresses ${reservedAddresses.join(', ')} are not correct addresses for an interface.`;
    }, (value) => areNotReservedAdresses(value, protocolVersion));
};
exports.isNotReservedHostIPAddress = isNotReservedHostIPAddress;
const isNotReservedHostDNSAddress = (protocolVersion) => {
    return Yup.string().test('is-not-reserved-dns-address', ({ value }) => {
        const addresses = value.split(',');
        if (addresses.length === 1) {
            return `The provided IP address is not a valid DNS address.`;
        }
        const reservedAddresses = addresses.filter((address) => {
            return isReservedAddress(address, protocolVersion);
        });
        return `The provided IP addresses ${reservedAddresses.join(', ')} are not valid DNS addresses.`;
    }, (value) => areNotReservedAdresses(value, protocolVersion));
};
exports.isNotReservedHostDNSAddress = isNotReservedHostDNSAddress;
const getIpAddressInSubnetValidationSchema = (protocolVersion, subnet) => {
    return Yup.string().test('is-in-subnet', `IP Address is outside of the subnet ${subnet}`, (value, testContext) => {
        if (!value) {
            return true;
        }
        const ipValidationSchema = (0, exports.getIpAddressValidationSchema)(protocolVersion);
        try {
            ipValidationSchema.validateSync(value);
        }
        catch (err) {
            const error = err;
            return testContext.createError({ message: error.message });
        }
        try {
            const inSubnet = (0, is_in_subnet_1.isInSubnet)(value, subnet);
            return inSubnet;
        }
        catch (err) {
            //if isInSubnet fails it means the machine network cidr isn't valid and this validation is irrelevant
            return true;
        }
    });
};
exports.getIpAddressInSubnetValidationSchema = getIpAddressInSubnetValidationSchema;
const getIpIsNotNetworkOrBroadcastAddressSchema = (protocolVersion, subnet) => {
    return Yup.string().test('is-not-network-or-broadcast', `The IP address must not match the network or broadcast address`, (value) => {
        // Allow both addresses for IPv4 /31 subnets (RFC 3021)
        if (protocolVersion === dataTypes_1.ProtocolVersion.ipv4 && subnet.endsWith('/31')) {
            return true;
        }
        const subnetAddr = (0, protocolVersion_1.getAddressObject)(subnet, protocolVersion);
        if (!subnetAddr) {
            return true;
        }
        else {
            const subnetStart = subnetAddr === null || subnetAddr === void 0 ? void 0 : subnetAddr.startAddress().correctForm();
            const subnetEnd = subnetAddr === null || subnetAddr === void 0 ? void 0 : subnetAddr.endAddress().correctForm();
            return !(value === subnetStart || value === subnetEnd);
        }
    });
};
exports.getIpIsNotNetworkOrBroadcastAddressSchema = getIpIsNotNetworkOrBroadcastAddressSchema;
//# sourceMappingURL=commonValidationSchemas.js.map