update-metadata.php 7.83 KB
Newer Older
1 2 3 4
<?php // Copyright (c) 2018, SWITCH
$MAN=<<<PAGE
Name:        SWITCHwayf
Author:      Lukas Haemmerle, SWITCH
5
Description: This script is used to dynamically create the list of
6 7
             IdPs and SP to be displayed for the WAYF/DS service 
             based on the federation metadata.
8
             It is intended to be run periodically, e.g. with a cron
9
             entry like:
10 11 12 13 14
             5 * * * * /usr/bin/php update-metadata.php \
                 --metadata-file /var/cache/shibboleth/metadata.switchaai.xml \
                 --metadata-idp-file /tmp/IDProvider.metadata.php \
                 --metadata-sp-file /tmp/SProvider.metadata.php \
                 > /dev/null
15
        
16 17
Usage
-----
18 19 20
php update-metadata.php -help|-h
php update-metadata.php --metadata-file <file> \
    --metadata-idp-file <file> --metadata-sp-file <file> \
21 22
    [--verbose | -v] [--min-sp-count <count>] [--min-idp-count <count>] \
    [--language <locale>] [--syslog] [--syslog-id <id>]
23 24
php update-metadata.php --metadata-url <url> \
    --metadata-idp-file <file> --metadata-sp-file <file> \
25 26
    [--verbose | -v] [--min-sp-count <count>] [--min-idp-count <count>] \
    [--language <locale>] [--syslog] [--syslog-id <id>]
27

28 29
Argument Description
--------------------
30
--metadata-url <url>        SAML2 metadata URL
31
--metadata-file <file>      SAML2 metadata file
32 33
--metadata-idp-file <file>  File containing service providers 
--metadata-sp-file <file>   File containing identity providers 
34 35
--min-idp-count <count>     Minimum expected number of IdPs in metadata
--min-sp-count <count>      Minimum expected number of SPs in metadata
36
--language <locale>         Language locale, e.g. 'en', 'jp', ...
37 38 39 40
--filter-idps-by-ec         Only process IdPs that are in given 
                            entity category. Multiple categories
                            can be provided space separated. 
                            If the IdP is in none, the IdP is ignored.
41
--syslog                    Use syslog for reporting
42
--syslog-id <id>            Process identity for syslog messages
43
--verbose | -v              Verbose mode
44
--help | -h                 Print this man page
45 46 47 48


PAGE;

49
$topLevelDir = dirname(__DIR__);
50 51 52

require_once($topLevelDir . '/lib/functions.php');
require_once($topLevelDir . '/lib/readMetadata.php');
53 54 55

// Script options
$longopts = array(
56
    "metadata-url:",
57 58 59
    "metadata-file:",
    "metadata-idp-file:",
    "metadata-sp-file:",
60 61
    "min-idp-count:",
    "min-sp-count:",
62
    "filter-idps-by-ec:",
63 64
    "language:",
    "verbose",
65
    "syslog",
66
    "syslog-id:",
67 68 69 70 71 72 73 74 75
    "help",
);

$options = getopt('hv', $longopts);

if (isset($options['help']) || isset($options['h'])) {
	exit($MAN);
} 

76 77 78 79
// simple options
$language = isset($options['language']) ? $options['language'] : 'en';
$verbose  = isset($options['verbose']) || isset($options['v']) ? true : false;
$syslog   = isset($options['syslog']) ? true : false;
80
$syslogId = isset($options['syslog-id']) ? $options['syslog-id'] : 'SWITCHwayf';
81

Guillaume Rousse's avatar
Guillaume Rousse committed
82 83 84 85
if ($syslog) {
	openlog($syslogId, LOG_NDELAY, LOG_USER);
}

86 87 88
if (isset($options['metadata-url'])) {
	$metadataURL = $options['metadata-url'];
} elseif (isset($options['metadata-file'])) {
89
	$metadataFile = $options['metadata-file'];
90
} else {
91
	reportError("Exiting: both --metadata-url and --metadata-file parameters missing\n");
92
	exit(1);
93 94 95
}

if (!isset($options['metadata-sp-file'])) {
96
	reportError("Exiting: mandatory --metadata-sp-file parameter missing\n");
97
	exit(1);
98 99 100 101 102 103
} else {
	$metadataSPFile = $options['metadata-sp-file'];
	$metadataTempSPFile = $metadataSPFile.'.swp';
}

if (!isset($options['metadata-idp-file'])) {
104
	reportError("Exiting: mandatory --metadata-idp-file parameter missing\n");
105
	exit(1);
106 107 108 109 110
} else {
	$metadataIDPFile = $options['metadata-idp-file'];
	$metadataTempIDPFile = $metadataIDPFile.'.swp';
}

111
if (isset($options['min-sp-count'])) {
112
	if (preg_match('/^(\d+)%$/', $options['min-sp-count'], $matches)) {
113 114 115 116 117 118 119
		if (file_exists($metadataSPFile)) {
			require_once($metadataSPFile);
			$SPCount = count($metadataSProviders);
			$minSPCount = floor($SPCount * $matches[1] / 100);
		} else {
			$minSPCount = 0;
		}
120
	} elseif (preg_match('/^\d+$/', $options['min-sp-count'])) {
121
		$minSPCount = $options['min-sp-count'];
122
	} else {
123 124
		reportError("Exiting: invalid value for --min-sp-count parameter\n");
		exit(1);
125 126 127 128 129 130
	}
} else {
	$minSPCount = 0;
}

if (isset($options['min-idp-count'])) {
131
	if (preg_match('/^(\d+)%$/', $options['min-idp-count'], $matches)) {
132 133 134 135 136 137 138
		if (file_exists($metadataIDPFile)) {
			require_once($metadataIDPFile);
			$IDPCount = count($metadataIDProviders);
			$minIDPCount = floor($IDPCount * $matches[1] / 100);
		} else {
			$minIDPCount = 0;
		}
139
	} elseif (preg_match('/^\d+$/', $options['min-idp-count'])) {
140
		$minIDPCount = $options['min-idp-count'];
141
	} else {
142 143
		reportError("Exiting: invalid value for --min-idp-count parameter\n");
		exit(1);
144 145 146 147 148
	}
} else {
	$minIDPCount = 0;
}

149 150 151 152 153 154
if(isset($options['filter-idps-by-ec'])){
	$filterEntityCategory = $options['filter-idps-by-ec'];
} else {
	$filterEntityCategory = false;
}

155
// Input validation
156
if (isset($metadataURL) && $metadataURL) {
157 158
	$metadataFile = tempnam(sys_get_temp_dir(), 'metadata');
	if (!ini_get('allow_url_fopen')) {
159
		reportError("Exiting: allow_url_fopen disabled, unabled to download $metadataURL\n");
160
		exit(1);
161 162
	}
	if ($verbose) {
Guillaume Rousse's avatar
Guillaume Rousse committed
163
		reportInfo("Downloading metadata file from $metadataURL\n");
164
	}
165
	$result = @copy($metadataURL, $metadataFile);
166 167
	if (!$result) {
		$error = error_get_last();
168
		$message = explode(': ', $error['message'])[2];
169
		reportError("Exiting: could not download $metadataURL: $message");
170
		exit(1);
171 172 173 174 175 176
	}
} else {
	if (
		!file_exists($metadataFile)
		|| filesize($metadataFile) == 0
		) {
177
		reportError("Exiting: file $metadataFile is empty or does not exist\n");
178
		exit(1);
179 180 181
	}

	if (!is_readable($metadataFile)){
182
		reportError("Exiting: file $metadataFile is not readable\n");
183
		exit(1);
184
	}
185 186 187
}

if ($verbose) {
188
	reportInfo("Parsing metadata file $metadataFile\n");
189 190 191 192 193 194 195
}

// Parse metadata
list($metadataIDProviders, $metadataSProviders) = parseMetadata($metadataFile, $language);

// If $metadataIDProviders is not FALSE, dump results in $metadataIDPFile.
if (is_array($metadataIDProviders)){
196 197
	$IDPCount = count($metadataIDProviders);
	if ($IDPCount < $minIDPCount) {
198
		reportError("Exiting: number of identity providers found ($IDPCount) lower than expected ($minIDPCount)\n");
199
		exit(1);
200
	}
201 202

	if ($verbose) {
203
		reportInfo("Dumping $IDPCount extracted identity providers to file $metadataIDPFile\n");
204 205 206 207
	}
	dumpFile($metadataTempIDPFile, $metadataIDProviders, 'metadataIDProviders');
	
	if(!rename($metadataTempIDPFile, $metadataIDPFile)){
208
		reportError("Exiting: could not rename temporary file $metadataTempIDPFile to $metadataIDPFile\n");
209
		exit(1);
210 211 212 213 214
	}
}

// If $metadataSProviders is not FALSE, dump results in $metadataSPFile.
if (is_array($metadataSProviders)){
215 216
	$SPCount = count($metadataSProviders);
	if ($SPCount < $minSPCount) {
217
		reportError("Exiting: number of service providers found ($SPCount) lower than expected ($minSPCount)\n");
218
		exit(1);
219
	}
220 221

	if ($verbose) {
222
		reportInfo("Dumping $SPCount extracted service providers to file $metadataSPFile\n");
223 224 225 226
	}
	dumpFile($metadataTempSPFile, $metadataSProviders, 'metadataSProviders');
	
	if(!rename($metadataTempSPFile, $metadataSPFile)){
227
		reportError("Exiting: could not rename temporary file $metadataTempSPFile to $metadataSPFile\n");
228
		exit(1);
229 230
	}
}
231 232

// clean up if needed
233
if (isset($metadataURL) && $metadataURL) {
234
	$result = @unlink($metadataFile);
235 236
	if (!$result) {
		$error = error_get_last();
237
		$message = $error['message'];
238
		reportError("Exiting: could not delete temporary file $metadataFile: $message");
239
		exit(1);
240 241
	}
}
242

243 244 245 246
if ($syslog) {
	closelog();
}

247
function reportError($message) {
Guillaume Rousse's avatar
Guillaume Rousse committed
248
	global $syslog;
249 250 251 252 253 254 255 256 257

	if ($syslog) {
		syslog(LOG_ERR, $message);
	} else {
		fwrite(STDERR, $message);
	}
}

function reportInfo($message) {
Guillaume Rousse's avatar
Guillaume Rousse committed
258
	global $syslog;
259 260 261 262 263 264 265

	if ($syslog) {
		syslog(LOG_INFO, $message);
	} else {
		fwrite(STDOUT, $message);
	}
}