@salesforce/core
Version:
Core libraries to interact with SFDX projects, orgs, and APIs.
210 lines • 8.73 kB
JavaScript
"use strict";
/*
* Copyright (c) 2021, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.SfdcUrl = void 0;
exports.getLoginAudienceCombos = getLoginAudienceCombos;
const node_url_1 = require("node:url");
const kit_1 = require("@salesforce/kit");
const ts_types_1 = require("@salesforce/ts-types");
const myDomainResolver_1 = require("../status/myDomainResolver");
const logger_1 = require("../logger/logger");
const lifecycleEvents_1 = require("../lifecycleEvents");
const envVars_1 = require("../config/envVars");
function getLoginAudienceCombos(audienceUrl, loginUrl) {
const filtered = [
[loginUrl, loginUrl],
[SfdcUrl.SANDBOX, SfdcUrl.SANDBOX],
[SfdcUrl.PRODUCTION, SfdcUrl.PRODUCTION],
[audienceUrl, audienceUrl],
[audienceUrl, SfdcUrl.PRODUCTION],
[audienceUrl, SfdcUrl.SANDBOX],
[loginUrl, audienceUrl],
[loginUrl, SfdcUrl.PRODUCTION],
[loginUrl, SfdcUrl.SANDBOX],
[SfdcUrl.PRODUCTION, audienceUrl],
[SfdcUrl.SANDBOX, audienceUrl],
].filter(([login, audience]) => !((login === SfdcUrl.PRODUCTION && audience === SfdcUrl.SANDBOX) ||
(login === SfdcUrl.SANDBOX && audience === SfdcUrl.PRODUCTION)));
const reduced = filtered.reduce((acc, [login, audience]) => {
const l = new node_url_1.URL(login);
const a = new node_url_1.URL(audience);
acc.set(`${l.origin}:${a.origin}`, [login, audience]);
return acc;
}, new Map());
return [...reduced.values()];
}
class SfdcUrl extends node_url_1.URL {
/**
* Salesforce URLs
*/
static SANDBOX = 'https://test.salesforce.com';
static PRODUCTION = 'https://login.salesforce.com';
static cache = new Set();
logger;
envVars;
constructor(input, base) {
super(input.toString(), base);
if (this.protocol !== 'https:' && !SfdcUrl.cache.has(this.origin)) {
SfdcUrl.cache.add(this.origin);
void lifecycleEvents_1.Lifecycle.getInstance().emitWarning(`Using insecure protocol: ${this.protocol} on url: ${this.origin}`);
}
this.envVars = new envVars_1.EnvVars();
}
static isValidUrl(input) {
try {
new node_url_1.URL(input.toString());
return true;
}
catch {
return false;
}
}
/**
* Returns the appropriate jwt audience url for this url
* Use SF_AUDIENCE_URL env var to override the audience url
*
* @param createdOrgInstance The Salesforce instance the org was created on. e.g. `cs42`
* @return {Promise<string>} The audience url
*/
async getJwtAudienceUrl(createdOrgInstance) {
this.logger = await logger_1.Logger.child('SfdcUrl');
// environment variable is used as an override
const envVarVal = this.envVars.getString('SF_AUDIENCE_URL', '');
if (envVarVal) {
this.logger.debug(`Audience URL overridden by env var SF_AUDIENCE_URL=${envVarVal}`);
return envVarVal;
}
if (Boolean(createdOrgInstance && /^gs1/gi.test(createdOrgInstance)) ||
/(gs1.my.salesforce.com)/gi.test(this.origin)) {
return 'https://gs1.salesforce.com';
}
return SfdcUrl.PRODUCTION;
}
/**
* Tests whether this url contains a Salesforce owned domain
*
* @return {boolean} true if this is a salesforce domain
*/
isSalesforceDomain() {
// Source https://help.salesforce.com/articleView?id=000003652&type=1
const allowlistOfSalesforceDomainPatterns = [
'.cloudforce.com',
'.content.force.com',
'.force.com',
'.salesforce.com',
'.salesforceliveagent.com',
'.secure.force.com',
'crmforce.mil',
'.sfcrmproducts.cn',
];
const allowlistOfSalesforceHosts = ['developer.salesforce.com', 'trailhead.salesforce.com'];
return allowlistOfSalesforceDomainPatterns.some((pattern) => this.hostname.endsWith(pattern) || allowlistOfSalesforceHosts.includes(this.hostname));
}
/**
* Tests whether this url is an internal Salesforce domain
*
* @returns {boolean} true if this is an internal domain
*/
isInternalUrl() {
const INTERNAL_URL_PARTS = [
'.vpod.',
'stm.salesforce.com',
'stm.force.com',
'.blitz.salesforce.com',
'.stm.salesforce.ms',
'.pc-rnd.force.com',
'.pc-rnd.salesforce.com',
'.crm.dev', // workspaces container
];
return (this.origin.startsWith('https://gs1.') ||
this.isLocalUrl() ||
INTERNAL_URL_PARTS.some((part) => this.origin.includes(part)));
}
/**
* Tests whether this url runs on a local machine
*
* @returns {boolean} true if this is a local machine
*/
isLocalUrl() {
const LOCAL_PARTS = ['localhost.sfdcdev.', '.internal.'];
return LOCAL_PARTS.some((part) => this.origin.includes(part));
}
toLightningDomain() {
if (this.origin.endsWith('.my.salesforce.mil')) {
return this.origin.replace('.my.salesforce.mil', '.lightning.crmforce.mil');
}
// enhanced domains
// ex: sandbox.my.salesforce.com, scratch.my.salesforce.com, etc
if (this.origin.endsWith('.my.salesforce.com')) {
return this.origin.replace('.my.salesforce.com', '.lightning.force.com');
}
// alternative domains
if (this.origin.endsWith('.my-salesforce.com')) {
return this.origin.replace('.my-salesforce.com', '.my-lightning.com');
}
// CN Specific domains for Alibaba Cloud
if (this.origin.endsWith('.my.sfcrmproducts.cn')) {
return this.origin.replace('.my.sfcrmproducts.cn', '.lightning.sfcrmapps.cn');
}
// all non-mil domains
return `https://${(0, ts_types_1.ensureArray)(/https?:\/\/([^.]*)/.exec(this.origin))
.slice(1, 2)
.pop()}.lightning.force.com`;
}
/**
* Tests whether this url has the lightning domain extension
* This method that performs the dns lookup of the host. If the lookup fails the internal polling (1 second), client will try again until timeout
* If SF_DOMAIN_RETRY environment variable is set (number) it overrides the default timeout duration (240 seconds)
*
* @returns {Promise<true | never>} The resolved ip address or never
* @throws {@link SfError} If can't resolve DNS.
*/
async checkLightningDomain() {
const quantity = (0, ts_types_1.ensureNumber)(this.envVars.getNumber('SF_DOMAIN_RETRY', 240));
const timeout = new kit_1.Duration(quantity, kit_1.Duration.Unit.SECONDS);
if (this.isInternalUrl() || timeout.seconds === 0) {
return true;
}
const resolver = await myDomainResolver_1.MyDomainResolver.create({
url: new node_url_1.URL(this.toLightningDomain()),
timeout,
frequency: new kit_1.Duration(1, kit_1.Duration.Unit.SECONDS),
});
await resolver.resolve();
return true;
}
/**
* Method that performs the dns lookup of the host. If the lookup fails the internal polling (1 second), client will try again until timeout
* If SF_DOMAIN_RETRY environment variable is set (number) it overrides the default timeout duration (240 seconds)
*
* @returns the resolved ip address.
* @throws {@link SfError} If can't resolve DNS.
*/
async lookup() {
const quantity = (0, ts_types_1.ensureNumber)(this.envVars.getNumber('SF_DOMAIN_RETRY', 240));
const timeout = new kit_1.Duration(quantity, kit_1.Duration.Unit.SECONDS);
const resolver = await myDomainResolver_1.MyDomainResolver.create({
url: new node_url_1.URL(this.origin),
timeout,
frequency: new kit_1.Duration(1, kit_1.Duration.Unit.SECONDS),
});
return resolver.resolve();
}
/**
* Test whether this url represents a lightning domain
*
* @returns {boolean} true if this domain is a lightning domain
*/
isLightningDomain() {
return (this.origin.includes('.lightning.force.com') ||
this.origin.includes('.lightning.crmforce.mil') ||
this.origin.includes('.lightning.sfcrmapps.cn'));
}
}
exports.SfdcUrl = SfdcUrl;
//# sourceMappingURL=sfdcUrl.js.map