*/ public static function hosts(): array { $entries = []; foreach ((array) config('admin_access.site_hosts', []) as $host) { $entries[] = ['host' => (string) $host, 'what' => 'site']; } // The host this page is being served from, always. // // Without it an installation that has not configured SITE_HOST — a // development box, or one reached by a name nobody wrote into the // config — listed ONLY the instance zone, and described it as the // customers' domain. So the page a visitor opened to check our // addresses omitted the address they had just typed, and named the // wrong thing as ours. Reported exactly that way. // // Deduplicated below by hostname, so a configured site host does not // appear twice. if (($current = request()?->getHost()) && ! in_array($current, array_column($entries, 'host'), true)) { $entries[] = ['host' => $current, 'what' => 'site']; } if ($app = (string) config('admin_access.app_host')) { $entries[] = ['host' => $app, 'what' => 'portal']; } if ($status = (string) config('admin_access.status_host')) { $entries[] = ['host' => $status, 'what' => 'status']; } // The instance zone as a pattern, because the label is per customer and // naming one customer's address on a public page names that customer. if ($zone = ProvisioningSettings::dnsZone()) { $entries[] = ['host' => '*.'.$zone, 'what' => 'instance']; } // The console is deliberately absent. It is not an address a customer // has any business visiting, and printing it on a public page tells an // attacker where the operator interface lives. return array_values(array_filter($entries, fn (array $e) => $e['host'] !== '')); } /** * The registrable domains behind those hosts — what somebody actually has * to check in the address bar. * * "Everything before the first slash ends in one of these" is a rule a * person can apply under time pressure; a list of eight hostnames is not. * * Keyed by domain, valued by what it is for — so the page never has to * work that out from the order it happens to come back in. * * @return array */ public static function registrable(): array { $domains = []; foreach (self::hosts() as $entry) { $labels = explode('.', ltrim($entry['host'], '*.')); // Last two labels. Not a public-suffix implementation: this runs // against an installation's own configured domains, which are known // quantities, and pulling in a suffix list to be right about // co.uk on a page that says "check the address bar" is not the // trade this needs. if (count($labels) < 2) { continue; } $domain = implode('.', array_slice($labels, -2)); // First writer wins: the site is listed before the instance zone, // so a single-domain installation is described as the company's // rather than as the customers'. $domains[$domain] ??= $entry['what'] === 'instance' ? 'instances' : 'company'; } return $domains; } }