summaryrefslogblamecommitdiffstats
path: root/modules-available/sysconfig/inc/shib.inc.php
blob: c39565720b4016aa2c158709b9d7e0b6d477bb9b (plain) (tree)





















































































































































                                                                                                               
<?php

class Shib
{
	const SHIB_LOOKUP_JSON = '/var/cache/slx-admin/idp2suffix.json';

	const SHIB_DISCO_JSON = '/var/cache/slx-admin/discofeed.json';

	const RET_ERR = 0;
	const RET_UPDATE = 1;
	const RET_UNCHANGED = 2;

	/**
	 * Updates the local copy of the JSON file for mapping IDP entityIDs to scope suffixes.
	 * If the list changed, all ShibAuth modules will be rebuilt.
	 *
	 * @return bool true on success, or if list didn't change. false otherwise.
	 */
	public static function refreshIdpSuffixMap(): bool
	{
		$changed = false;
		$ret = self::updateFromUrl(CONFIG_SHIB_LOOKUP_URL . '?what=id2suffix', self::SHIB_LOOKUP_JSON);
		if ($ret === self::RET_ERR)
			return false;
		if ($ret === self::RET_UPDATE) {
			$changed = true;
		}
		$ret = self::updateFromUrl(CONFIG_SHIB_DISCO_URL, self::SHIB_DISCO_JSON);
		if ($ret === self::RET_ERR)
			return false;
		if ($ret === self::RET_UPDATE) {
			$changed = true;
		}
		if ($changed) {
			// Rebuild any modules in the background
			$configs = [];
			$list = ConfigModule::getAll('ShibAuth');
			$parent = null;
			foreach ($list as $mod) {
				$r = $mod->generate(false, $parent);
				if (is_string($r)) {
					$parent = $r;
				}
				foreach (ConfigTgz::getAllForModule($mod->id()) as $tgz) {
					$configs[$tgz->id()] = $tgz;
				}
			}
			foreach ($configs as $tgz) {
				$r = $tgz->generate(false, 0, $parent);
				if (is_string($r)) {
					$parent = $r;
				}
			}
		}
		return $ret;
	}

	private static function updateFromUrl(string $url, string $file): int
	{
		if (!file_exists($file) || filemtime($file) + 86400 < time()) {
			$code = null;
			$ret = Download::toFile($file . '.tmp',
				$url, 15, $code);
			if (!$ret)
				return self::RET_ERR;
			if (!file_exists($file)
				|| filesize($file) !== filesize($file . '.tmp')
				|| sha1_file($file, true) !== sha1_file($file . '.tmp', true)
			) {
				rename($file . '.tmp', $file);
				if ($code >= 200 && $code < 300)
					return self::RET_UPDATE;
				return self::RET_ERR; // Not 2xx range, keep file but return error
			}
			unlink($file . '.tmp');
		}
		return self::RET_UNCHANGED;
	}

	/**
	 * @return ?array of registrars (key) containing array{"registrar": string, "list": string[]}
	 */
	public static function getListByRegistrar(): ?array
	{
		$disco = json_decode(file_get_contents(self::SHIB_DISCO_JSON), true);
		$lookup = json_decode(file_get_contents(self::SHIB_LOOKUP_JSON), true);
		if (!is_array($disco) || !is_array($lookup)) {
			return null;
		}
		foreach ($disco as $item) {
			if (!isset($item['entityID'])) {
				continue;
			}
			$id = $item['entityID'];
			$name = null;
			$fallback = null;
			foreach ($item['DisplayNames'] ?? [] as $dn) {
				if ($dn['lang'] === LANG) {
					$name = $dn['value'];
				} elseif ($name === null && $dn['lang'] === 'en') {
					$name = $dn['value'];
				} elseif ($fallback === null) {
					$fallback = $dn['value'];
				}
			}
			if ($name === null) {
				$name = $fallback;
			}
			$registrar = $lookup[$id]['regauth'] ?? 'unknown';
			if (!isset($return[$registrar])) {
				$return[$registrar] = ['list' => [], 'registrar' => $registrar];
			}
			$return[$registrar]['list'][] = [
				'id' => $id,
				'name' => $name ?? $id,
			];
		}
		return $return;
	}

	public static function getIdp2SuffixList(): ?array
	{
		$lookup = json_decode(file_get_contents(self::SHIB_LOOKUP_JSON), true);
		if (!is_array($lookup))
			return null;
		return $lookup;
	}

	/**
	 * Map given list of registrar IDs back to all the IdP entityIDs they cover.
	 * @param string[] $regs
	 * @return string[]
	 */
	public static function explodeRegistrars(array $regs): array
	{
		if (empty($regs))
			return [];
		$idps = [];
		$reg2idps = Shib::getListByRegistrar();
		foreach ($regs as $reg) {
			if (!isset($reg2idps[$reg]['list']))
				continue;
			foreach ($reg2idps[$reg]['list'] as $elem) {
				$idps[] = $elem['id'];
			}
		}
		return $idps;
	}

}