This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
1 | <?php |
||
2 | |||
3 | /* |
||
4 | * ***************************************************************************** |
||
5 | * Contributions to this work were made on behalf of the GÉANT project, a |
||
6 | * project that has received funding from the European Union’s Framework |
||
7 | * Programme 7 under Grant Agreements No. 238875 (GN3) and No. 605243 (GN3plus), |
||
8 | * Horizon 2020 research and innovation programme under Grant Agreements No. |
||
9 | * 691567 (GN4-1) and No. 731122 (GN4-2). |
||
10 | * On behalf of the aforementioned projects, GEANT Association is the sole owner |
||
11 | * of the copyright in all material which was developed by a member of the GÉANT |
||
12 | * project. GÉANT Vereniging (Association) is registered with the Chamber of |
||
13 | * Commerce in Amsterdam with registration number 40535155 and operates in the |
||
14 | * UK as a branch of GÉANT Vereniging. |
||
15 | * |
||
16 | * Registered office: Hoekenrode 3, 1102BR Amsterdam, The Netherlands. |
||
17 | * UK branch address: City House, 126-130 Hills Road, Cambridge CB2 1PQ, UK |
||
18 | * |
||
19 | * License: see the web/copyright.inc.php file in the file structure or |
||
20 | * <base_url>/copyright.php after deploying the software |
||
21 | */ |
||
22 | |||
23 | namespace web\lib\admin; |
||
24 | |||
25 | use Exception; |
||
26 | |||
27 | /** |
||
28 | * This class provides various HTML snippets and other UI-related convenience functions. |
||
29 | * |
||
30 | * @author Stefan Winter <[email protected]> |
||
31 | */ |
||
32 | class UIElements extends \core\common\Entity { |
||
33 | |||
34 | /** |
||
35 | * the custom displayable variant of the term 'federation' |
||
36 | * |
||
37 | * @var string |
||
38 | */ |
||
39 | public $nomenclatureFed; |
||
40 | |||
41 | /** |
||
42 | * the custom displayable variant of the term 'institution' |
||
43 | * |
||
44 | * @var string |
||
45 | */ |
||
46 | public $nomenclatureIdP; |
||
47 | |||
48 | /** |
||
49 | * the custom displayable variant of the term 'hotspot' |
||
50 | * |
||
51 | * @var string |
||
52 | */ |
||
53 | public $nomenclatureHotspot; |
||
54 | |||
55 | /** |
||
56 | * the custom displayable variant of the term 'hotspot' |
||
57 | * |
||
58 | * @var string |
||
59 | */ |
||
60 | public $nomenclatureParticipant; |
||
61 | |||
62 | /** |
||
63 | * Initialises the class. |
||
64 | * |
||
65 | * Mainly fetches various nomenclature from the config and attempts to translate those into local language. Needs pre-loading some terms. |
||
66 | */ |
||
67 | public function __construct() { |
||
68 | // pick up the nomenclature translations from core - no need to repeat |
||
69 | // them here in this catalogue |
||
70 | parent::__construct(); |
||
71 | $this->nomenclatureFed = \core\common\Entity::$nomenclature_fed; |
||
72 | $this->nomenclatureIdP = \core\common\Entity::$nomenclature_idp; |
||
73 | $this->nomenclatureHotspot = \core\common\Entity::$nomenclature_hotspot; |
||
74 | $this->nomenclatureParticipant = \core\common\Entity::$nomenclature_participant; |
||
75 | } |
||
76 | |||
77 | /** |
||
78 | * provides human-readable text for the various option names as stored in DB. |
||
79 | * |
||
80 | * @param string $input raw text in need of a human-readable display variant |
||
81 | * @return string the human-readable variant |
||
82 | * @throws \Exception |
||
83 | */ |
||
84 | public function displayName($input, $fullDisplay = false) { |
||
85 | \core\common\Entity::intoThePotatoes(); |
||
86 | $ssidText = _("SSID"); |
||
87 | $passpointOiText = _("HS20 Consortium OI"); |
||
88 | |||
89 | if (!empty(\config\ConfAssistant::CONSORTIUM['interworking-consortium-oi']) && count(\config\ConfAssistant::CONSORTIUM['interworking-consortium-oi']) > 0) { |
||
90 | $passpointOiText = _("Additional HS20 Consortium OI"); |
||
91 | } |
||
92 | |||
93 | $displayNames = [ |
||
94 | "support:url" => ['display' => _("Support: Web"), 'help' => ""], |
||
95 | "support:eap_types" => ['display' => _("Support: EAP Types"), 'help' => ""], |
||
96 | "support:phone" => ['display' => _("Support: Phone"), 'help' => ""], |
||
97 | "support:email" => ['display' => _("Support: E-Mail"), 'help' => ""], |
||
98 | "general:instname" => ['display' => _("Organisation Name"), 'help' => ""], |
||
99 | "general:instshortname" => ['display' => _("Organisation Acronym"), 'help' => ""], |
||
100 | "general:instaltname" => ['display' => _("Organisation Alt Name"), 'help' => ""], |
||
101 | "general:geo_coordinates" => ['display' => _("Location"), 'help' => ""], |
||
102 | "general:logo_url" => ['display' => _("Logo URL"), 'help' => ""], |
||
103 | "general:logo_file" => ['display' => _("Logo image"), 'help' => ""], |
||
104 | "media:wired" => ['display' => _("Configure Wired Ethernet"), 'help' => ""], |
||
105 | "eap:server_name" => ['display' => _("Name (CN) of Authentication Server"), 'help' => ""], |
||
106 | "eap:ca_vailduntil" => ['display' => _("Valid until"), 'help' => ""], |
||
107 | "eap:enable_nea" => ['display' => _("Enable device assessment"), 'help' => ""], |
||
108 | "support:info_file" => ['display' => _("Terms of Use"), 'help' => ""], |
||
109 | "eap:ca_url" => ['display' => _("CA Certificate URL"), 'help' => ""], |
||
110 | "eap:ca_file" => ['display' => _("CA Certificate File"), 'help' => ""], |
||
111 | "profile:name" => ['display' => _("Profile Display Name"), 'help' => ""], |
||
112 | "profile:production" => ['display' => _("Production-Ready"), 'help' => ""], |
||
113 | "hiddenprofile:tou_accepted" => ['display' => _("Admin Accepted IdP Terms of Use"), 'help' => ""], |
||
114 | "hiddenmanagedsp:tou_accepted" => ['display' => _("Admin Accepted SP Terms of Use"), 'help' => ""], |
||
115 | "device-specific:customtext" => ['display' => _("Extra text on downloadpage for device"), 'help' => ""], |
||
116 | "device-specific:redirect" => ['display' => _("Redirection Target"), 'help' => ""], |
||
117 | "eap-specific:customtext" => ['display' => _("Extra text on downloadpage for device"), 'help' => ""], |
||
118 | "eap-specific:tls_use_other_id" => ['display' => _("Turn on selection of EAP-TLS User-Name"), 'help' => ""], |
||
119 | "device-specific:geantlink" => ['display' => _("Use GEANTlink for TTLS (Windows 8 and 10)"), 'help' => ""], |
||
120 | "device-specific:geteduroam" => ['display' => _("Show the dedicated geteduroam download page for this device"), 'help' => ""], |
||
121 | "profile:description" => ['display' => _("Profile Description"), 'help' => ""], |
||
122 | "profile:customsuffix" => ['display' => _("Custom Installer Name Suffix"), 'help' => ""], |
||
123 | "media:openroaming" => ['display' => _("OpenRoaming"), 'help' => ""], |
||
124 | "user:fedadmin" => ['display' => sprintf(_("%s Administrator"), $this->nomenclatureFed), 'help' => ""], |
||
125 | "user:realname" => ['display' => _("Real Name"), 'help' => ""], |
||
126 | "user:email" => ['display' => _("E-Mail Address"), 'help' => ""], |
||
127 | "media:remove_SSID" => ['display' => _("Remove/Disable SSID"), 'help' => ""], |
||
128 | "media:force_proxy" => ['display' => _("Mandatory Content Filtering Proxy"), 'help' => ""], |
||
129 | "fed:css_file" => ['display' => _("Custom CSS file for User Area"), 'help' => "not available"], |
||
130 | "fed:logo_file" => [ |
||
131 | 'display' => sprintf(_("%s Logo"), $this->nomenclatureFed), |
||
132 | 'help' => _("Your federation logo to be shown on CAT download pages and also on Windows installers if" |
||
133 | . " the option to include branding in installers is set as well.")], |
||
134 | "fed:desired_skin" => ['display' => _("Preferred Skin for User Area"), 'help' => "not available"], |
||
135 | "fed:include_logo_installers" => [ |
||
136 | 'display' => sprintf(_("Include %s branding in installers"), $this->nomenclatureFed), |
||
137 | 'help' => _("Add your federation logo to Windows installers.")], |
||
138 | "fed:realname" => [ |
||
139 | 'display' => sprintf(_("%s Name"), $this->nomenclatureFed), |
||
140 | 'help' => "The name of your federation."], |
||
141 | "fed:url" => ['display' => sprintf(_("%s Homepage"), $this->nomenclatureFed), 'help' => ""], |
||
142 | "fed:custominvite" => [ |
||
143 | 'display' => sprintf(_("Custom text in %s Invitations"), $this->nomenclatureParticipant), |
||
144 | 'help' => _("Your text in invitation mails sent for new IdP")], |
||
145 | "fed:silverbullet" => ['display' => sprintf(_("Enable %s"), \config\ConfAssistant::SILVERBULLET['product_name']), 'help' => ""], |
||
146 | "fed:silverbullet-noterm" => ['display' => sprintf(_("%s: Do not terminate EAP"), \core\ProfileSilverbullet::PRODUCTNAME), 'help' => ""], |
||
147 | "fed:silverbullet-maxusers" => ['display' => sprintf(_("%s: max users per profile"), \core\ProfileSilverbullet::PRODUCTNAME), 'help' => ""], |
||
148 | "fed:minted_ca_file" => [ |
||
149 | 'display' => sprintf(_("Mint %s with CA on creation"), $this->nomenclatureIdP), |
||
150 | 'help' => _("Set of default CAs to add to new IdPs on signup")], |
||
151 | "fed:openroaming" => [ |
||
152 | 'display' => sprintf(_("OpenRoaming: Allow %s Opt-In"),$this->nomenclatureParticipant), |
||
153 | 'help' => _("Allow IdP to set OpenRoaming support for its users.")], |
||
154 | "fed:openroaming_customtarget" => ['display' => _("OpenRoaming: Custom NAPTR Target"), 'help' => ""], |
||
155 | "fed:autoregister-synced" => [ |
||
156 | 'display' => _("Self registration from eduroam DB: add listed admins to CAT institutions"), |
||
157 | 'help' => sprintf(_("With this option turned on if a CAT institution is synced to the eduroam DB it is possible to have automatic enlisting of CAT institution admins under some conditions described <a href='%s'>here</a>."), "https://wiki.eduroam.org/")], |
||
158 | "fed:autoregister-new-inst" => [ |
||
159 | 'display' => _("Self registration from eduroam DB: allow creating new institutions"), |
||
160 | 'help' => sprintf(_("Turn this on and eduroam DB listed institution admins will be allowed to create new institutions under some conditions described <a href='%s'>here</a>."), "https://wiki.eduroam.org/")], |
||
161 | "fed:autoregister-entitlement" => [ |
||
162 | 'display' => _("Self registration based on entitlement: add admins to CAT institutions"), |
||
163 | 'help' => _("With this option turned on the system will verify the eduGAIN login of the potential administrator and propose taking control over institutions which use the realm within the scope defined in the user's pairwise-id attribute.") |
||
164 | ], |
||
165 | "fed:entitlement-attr" => [ |
||
166 | 'display' => _("Custom entitlement value for self-registration"), |
||
167 | 'help' => _("If you want to use the SAML eduPersonEntitlement based self-registration you may define a value that will be used in your federation for institutions admins. When this is not set the default value geant:eduroam:inst:admin will be used. This option makes sense only if you have 'Self registration based on entitlement' set") |
||
168 | ], |
||
169 | "media:SSID" => ['display' => $ssidText, 'help' => ""], |
||
170 | "media:consortium_OI" => ['display' => $passpointOiText, 'help' => ""], |
||
171 | "managedsp:guest_vlan" => ['display' => _("VLAN for guests"), 'help' => ""], |
||
172 | "managedsp:vlan" => ['display' => _("VLAN for own users"), 'help' => ""], |
||
173 | "managedsp:realmforvlan" => ['display' => _("Realm to be considered own users"), 'help' => ""], |
||
174 | "managedsp:operatorname" => ['display' => _("Custom Operator-Name attribute"), 'help' => ""], |
||
175 | ]; |
||
176 | |||
177 | if (!isset($displayNames[$input])) { // this is an error! throw an Exception |
||
178 | throw new \Exception("The translation of an option name was requested, but the option is not known to the system: " . htmlentities($input)); |
||
179 | } |
||
180 | \core\common\Entity::outOfThePotatoes(); |
||
181 | // none of the strings have HTML in them, only translators can provide own text for it -> no threat, but complained about by the security review |
||
182 | if ($fullDisplay) { |
||
183 | return ['display' => htmlspecialchars($displayNames[$input]['display']), 'help' => $displayNames[$input]['help']]; |
||
0 ignored issues
–
show
Bug
Best Practice
introduced
by
![]() |
|||
184 | } else { |
||
185 | return htmlspecialchars($displayNames[$input]['display']); |
||
186 | } |
||
187 | } |
||
188 | |||
189 | /** |
||
190 | * creates an HTML information block with a list of options from a given category and level |
||
191 | * @param array $optionlist list of options |
||
192 | * @param string $class option class of interest |
||
193 | * @param string $level option level of interest |
||
194 | * @return string HTML code |
||
195 | */ |
||
196 | public function infoblock(array $optionlist, string $class, string $level) { |
||
197 | \core\common\Entity::intoThePotatoes(); |
||
198 | $locationMarkers = []; |
||
199 | $retval = ""; |
||
200 | $optioninfo = \core\Options::instance(); |
||
201 | |||
202 | foreach ($optionlist as $option) { |
||
203 | $type = $optioninfo->optionType($option['name']); |
||
204 | if (preg_match('/^' . $class . '/', $option['name']) && $option['level'] == "$level") { |
||
205 | // all non-multilang attribs get this assignment ... |
||
206 | $language = ""; |
||
207 | $content = $option['value']; |
||
208 | // ... override them with multilang tags if needed |
||
209 | if ($type["flag"] == "ML") { |
||
210 | $language = _("default/other languages"); |
||
211 | if ($option['lang'] != 'C') { |
||
212 | $language = \config\Master::LANGUAGES[$option['lang']]['display'] ?? "(unsupported language)"; |
||
213 | } |
||
214 | } |
||
215 | |||
216 | switch ($type["type"]) { |
||
217 | case "coordinates": |
||
218 | $coords = json_decode($option['value'], true); |
||
219 | $locationMarkers[] = $coords; |
||
220 | break; |
||
221 | case "file": |
||
222 | $retval .= "<tr><td>" . $this->displayName($option['name']) . "</td><td>$language</td><td>"; |
||
223 | switch ($option['name']) { |
||
224 | case "general:logo_file": |
||
225 | case "fed:logo_file": |
||
226 | $retval .= $this->previewImageinHTML('ROWID-' . $option['level'] . '-' . $option['row_id']); |
||
227 | break; |
||
228 | case "eap:ca_file": |
||
229 | // fall-through intended: display both the same way |
||
230 | case "fed:minted_ca_file": |
||
231 | $retval .= $this->previewCAinHTML('ROWID-' . $option['level'] . '-' . $option['row_id']); |
||
232 | break; |
||
233 | case "support:info_file": |
||
234 | $retval .= $this->previewInfoFileinHTML('ROWID-' . $option['level'] . '-' . $option['row_id']); |
||
235 | break; |
||
236 | default: |
||
237 | } |
||
238 | break; |
||
239 | case "boolean": |
||
240 | if ($option['name'] == "fed:silverbullet" && \config\Master::FUNCTIONALITY_LOCATIONS['CONFASSISTANT_SILVERBULLET'] == "LOCAL" && \config\Master::FUNCTIONALITY_LOCATIONS['CONFASSISTANT_RADIUS'] != "LOCAL") { |
||
241 | // do not display the option at all; it gets auto-set by the ProfileSilverbullet constructor and doesn't have to be seen |
||
242 | break; |
||
243 | } |
||
244 | $retval .= "<tr><td>" . $this->displayName($option['name']) . "</td><td>$language</td><td><strong>" . ($content == "on" ? _("on") : _("off") ) . "</strong></td></tr>"; |
||
245 | break; |
||
246 | default: |
||
247 | $retval .= "<tr><td>" . $this->displayName($option['name']) . "</td><td>$language</td><td><strong>$content</strong></td></tr>"; |
||
248 | } |
||
249 | } |
||
250 | } |
||
251 | if (count($locationMarkers)) { |
||
252 | $marker = '<markers>'; |
||
253 | $locationCount = 0; |
||
254 | foreach ($locationMarkers as $g) { |
||
255 | $locationCount++; |
||
256 | $marker .= '<marker name="' . $locationCount . '" lat="' . $g['lat'] . '" lng="' . $g['lon'] . '" />'; |
||
257 | } |
||
258 | $marker .= '<\/markers>'; // some validator says this should be escaped |
||
259 | $jMarker = json_encode($locationMarkers); |
||
260 | $retval .= '<tr><td><script>markers=\'' . $marker . '\'; jmarkers = \'' . $jMarker . '\';</script></td><td></td><td></td></tr>'; |
||
261 | } |
||
262 | \core\common\Entity::outOfThePotatoes(); |
||
263 | return $retval; |
||
264 | } |
||
265 | |||
266 | /** |
||
267 | * creates HTML code to display all information boxes for an IdP |
||
268 | * |
||
269 | * @param \core\IdP $myInst the IdP in question |
||
270 | * @return string HTML code |
||
271 | */ |
||
272 | public function instLevelInfoBoxes(\core\IdP $myInst) { |
||
273 | \core\common\Entity::intoThePotatoes(); |
||
274 | $idpoptions = $myInst->getAttributes(); |
||
275 | $retval = "<div class='infobox'> |
||
276 | <h2>" . sprintf(_("General %s details"), $this->nomenclatureParticipant) . "</h2> |
||
277 | <table> |
||
278 | <tr> |
||
279 | <td> |
||
280 | " . _("Country:") . " |
||
281 | </td> |
||
282 | <td> |
||
283 | </td> |
||
284 | <td> |
||
285 | <strong>"; |
||
286 | $myFed = new \core\Federation($myInst->federation); |
||
287 | $retval .= $myFed->name; |
||
288 | $retval .= "</strong> |
||
289 | </td> |
||
290 | </tr>" . $this->infoblock($idpoptions, "general", "IdP") . " |
||
291 | </table> |
||
292 | </div>"; |
||
293 | |||
294 | $blocks = [["support", _("Global Helpdesk Details")], ["media", _("Media Properties")]]; |
||
295 | foreach ($blocks as $block) { |
||
296 | $retval .= "<div class='infobox'> |
||
297 | <h2>" . $block[1] . "</h2> |
||
298 | <table>" . |
||
299 | $this->infoblock($idpoptions, $block[0], "IdP") . |
||
300 | "</table> |
||
301 | </div>"; |
||
302 | } |
||
303 | \core\common\Entity::outOfThePotatoes(); |
||
304 | return $retval; |
||
305 | } |
||
306 | |||
307 | /** |
||
308 | * pretty-prints a file size number in SI "bi" units |
||
309 | * @param int $number the size of the file |
||
310 | * @return string the pretty-print representation of the file size |
||
311 | */ |
||
312 | private function displaySize(int $number) { |
||
313 | if ($number > 1024 * 1024) { |
||
314 | return round($number / 1024 / 1024, 2) . " MiB"; |
||
315 | } |
||
316 | if ($number > 1024) { |
||
317 | return round($number / 1024, 2) . " KiB"; |
||
318 | } |
||
319 | return $number . " B"; |
||
320 | } |
||
321 | |||
322 | /** |
||
323 | * |
||
324 | * @param string $table the database table |
||
325 | * @param integer $rowindex the database row_id |
||
326 | * @param boolean $checkpublic should we check if the requested piece of data is public? |
||
327 | * @return string|boolean the requested data, or FALSE if something went wrong |
||
328 | */ |
||
329 | public static function getBlobFromDB($table, $rowindex, $checkpublic) { |
||
330 | // the data is either public (just give it away) or not; in this case, only |
||
331 | // release if the data belongs to admin himself |
||
332 | if ($checkpublic) { |
||
333 | |||
334 | $owners = \core\EntityWithDBProperties::isDataRestricted($table, $rowindex); |
||
335 | |||
336 | $ownersCondensed = []; |
||
337 | |||
338 | if ($owners !== FALSE) { // restricted data, see if we're authenticated and owners of the data |
||
339 | $auth = new \web\lib\admin\Authentication(); |
||
340 | if (!$auth->isAuthenticated()) { |
||
341 | return FALSE; // admin-only, but we are not an admin |
||
342 | } |
||
343 | // we might be called without session context (filepreview) so get the |
||
344 | // context if needed |
||
345 | \core\CAT::sessionStart(); |
||
346 | |||
347 | foreach ($owners as $oneowner) { |
||
348 | $ownersCondensed[] = $oneowner['ID']; |
||
349 | } |
||
350 | if (array_search($_SESSION['user'], $ownersCondensed) === FALSE) { |
||
351 | return FALSE; // wrong guy |
||
352 | } |
||
353 | // carry on and get the data |
||
354 | } |
||
355 | } |
||
356 | |||
357 | $blob = \core\EntityWithDBProperties::fetchRawDataByIndex($table, $rowindex); |
||
358 | return $blob; // this means we might return FALSE here if something was wrong with the original requested reference |
||
359 | } |
||
360 | |||
361 | /** |
||
362 | * creates HTML code to display a nice UI representation of a CA |
||
363 | * |
||
364 | * @param string $cAReference ROWID pointer to the CA to display |
||
365 | * @return string HTML code |
||
366 | */ |
||
367 | public function previewCAinHTML($cAReference) { |
||
368 | \core\common\Entity::intoThePotatoes(); |
||
369 | $validator = new \web\lib\common\InputValidation(); |
||
370 | $ref = $validator->databaseReference($cAReference); |
||
371 | $caExpiryTrashhold = \config\ConfAssistant::CERT_WARNINGS['expiry_warning']; |
||
372 | $rawResult = UIElements::getBlobFromDB($ref['table'], $ref['rowindex'], FALSE); |
||
373 | if (is_bool($rawResult)) { // we didn't actually get a CA! |
||
374 | $retval = "<div class='ca-summary'>" . _("There was an error while retrieving the certificate from the database!") . "</div>"; |
||
375 | \core\common\Entity::outOfThePotatoes(); |
||
376 | return $retval; |
||
377 | } |
||
378 | $cAblob = base64_decode($rawResult); |
||
379 | |||
380 | $func = new \core\common\X509; |
||
381 | $details = $func->processCertificate($cAblob); |
||
382 | if ($details === FALSE) { |
||
383 | $retval = _("There was an error processing the certificate!"); |
||
384 | \core\common\Entity::outOfThePotatoes(); |
||
385 | return $retval; |
||
386 | } |
||
387 | |||
388 | $details['name'] = preg_replace('/(.)\/(.)/', "$1<br/>$2", $details['name']); |
||
389 | $details['name'] = preg_replace('/\//', "", $details['name']); |
||
390 | $certstatus = ( $details['root'] == 1 ? "R" : "I"); |
||
391 | $certTooltip = ( $details['root'] == 1 ? _("Root CA") : _("Intermediate CA")); |
||
392 | $innerbgColor = "#0000ff"; |
||
393 | $leftBorderColor = "#00ff00"; |
||
394 | $message = ""; |
||
395 | if ($details['ca'] == 0 && $details['root'] != 1) { |
||
396 | $leftBorderColor = "red"; |
||
397 | $message = _("This is a <strong>SERVER</strong> certificate!"); |
||
398 | if (\config\ConfAssistant::CERT_GUIDELINES !== '') { |
||
399 | $message .= "<br/><a target='_blank' href='".\config\ConfAssistant::CERT_GUIDELINES."'>". _("more info")."</a>"; |
||
400 | } |
||
401 | $message .= "<br/>"; |
||
402 | $retval = "<div class='ca-summary' style='border-left-color: $leftBorderColor'><div style='position:absolute; right: -15px; width:20px; height:20px; background-color:$innerbgColor; border-radius:10px; text-align: center;'><div style='padding-top:3px; font-weight:bold; color:#ffffff;'>S</div></div>" . $message . $details['name'] . "</div>"; |
||
403 | \core\common\Entity::outOfThePotatoes(); |
||
404 | return $retval; |
||
405 | } |
||
406 | $now = time(); |
||
407 | if ($now + \config\ConfAssistant::CERT_WARNINGS['expiry_critical'] > $details['full_details']['validTo_time_t']) { |
||
408 | $leftBorderColor = "red"; |
||
409 | $message = _("Certificate expired!") . "<br>"; |
||
410 | } elseif($now + \config\ConfAssistant::CERT_WARNINGS['expiry_warning'] > $details['full_details']['validTo_time_t'] - $caExpiryTrashhold) { |
||
411 | if ($leftBorderColor == "#00ff00") { |
||
412 | $leftBorderColor = "yellow"; |
||
413 | } |
||
414 | $message = _("Certificate close to expiry!") . "<br/>"; |
||
415 | } |
||
416 | |||
417 | if ($details['root'] == 1 && $details['basicconstraints_set'] == 0) { |
||
418 | if ($leftBorderColor == "#00ff00") { |
||
419 | $leftBorderColor = "yellow"; |
||
420 | } |
||
421 | $message .= "<div style='max-width: 25em'><strong>" . _("Improper root certificate, required critical CA extension missing, will not reliably install!") . "</strong>"; |
||
422 | if (\config\ConfAssistant::CERT_GUIDELINES !== '') { |
||
423 | $message .= "<br/><a target='_blank' href='".\config\ConfAssistant::CERT_GUIDELINES."'>". _("more info")."</a>"; |
||
424 | } |
||
425 | $message .= "</div><br/>"; |
||
426 | } |
||
427 | $retval = "<div class='ca-summary' style='border-left-color: $leftBorderColor'><div style='position:absolute; right: -15px; width:20px; height:20px; background-color:$innerbgColor; border-radius:10px; text-align: center;'><div title='$certTooltip' style='padding-top:3px; font-weight:bold; color:#ffffff;'>$certstatus</div></div>" . $message . $details['name'] . "<br>" . $this->displayName('eap:ca_vailduntil') . " " . gmdate('Y-m-d H:i:s', $details['full_details']['validTo_time_t']) . " UTC</div>"; |
||
428 | \core\common\Entity::outOfThePotatoes(); |
||
429 | return $retval; |
||
430 | } |
||
431 | |||
432 | /** |
||
433 | * creates HTML code to display a nice UI representation of an image |
||
434 | * |
||
435 | * @param string $imageReference ROWID pointer to the image to display |
||
436 | * @return string HTML code |
||
437 | */ |
||
438 | public function previewImageinHTML($imageReference) { |
||
439 | \core\common\Entity::intoThePotatoes(); |
||
440 | $retval = "<img style='max-width:150px' src='inc/filepreview.php?id=" . $imageReference . "' alt='" . _("Preview of logo file") . "'/>"; |
||
441 | \core\common\Entity::outOfThePotatoes(); |
||
442 | return $retval; |
||
443 | } |
||
444 | |||
445 | /** |
||
446 | * creates HTML code to display a nice UI representation of a TermsOfUse file |
||
447 | * |
||
448 | * @param string $fileReference ROWID pointer to the file to display |
||
449 | * @return string HTML code |
||
450 | */ |
||
451 | public function previewInfoFileinHTML($fileReference) { |
||
452 | \core\common\Entity::intoThePotatoes(); |
||
453 | $validator = new \web\lib\common\InputValidation(); |
||
454 | $ref = $validator->databaseReference($fileReference); |
||
455 | $fileBlob = UIElements::getBlobFromDB($ref['table'], $ref['rowindex'], FALSE); |
||
456 | if (is_bool($fileBlob)) { // we didn't actually get a file! |
||
457 | $retval = "<div class='ca-summary'>" . _("There was an error while retrieving the file from the database!") . "</div>"; |
||
458 | \core\common\Entity::outOfThePotatoes(); |
||
459 | return $retval; |
||
460 | } |
||
461 | $decodedFileBlob = base64_decode($fileBlob); |
||
462 | $fileinfo = new \finfo(); |
||
463 | $retval = "<div class='ca-summary'>" . _("File exists") . " (" . $fileinfo->buffer($decodedFileBlob, FILEINFO_MIME_TYPE) . ", " . $this->displaySize(strlen($decodedFileBlob)) . ")<br/><a href='inc/filepreview.php?id=$fileReference'>" . _("Preview") . "</a></div>"; |
||
464 | \core\common\Entity::outOfThePotatoes(); |
||
465 | return $retval; |
||
466 | } |
||
467 | |||
468 | /** |
||
469 | * creates HTML code for a UI element which informs the user about something. |
||
470 | * |
||
471 | * @param int $level what kind of information is to be displayed? |
||
472 | * @param string $text the text to display |
||
473 | * @param string $caption the caption to display |
||
474 | * @param bool $omittabletags the output usually has tr/td table tags, this option suppresses them |
||
475 | * @return string |
||
476 | */ |
||
477 | public function boxFlexible(int $level, string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) { |
||
478 | \core\common\Entity::intoThePotatoes(); |
||
479 | $uiMessages = [ |
||
480 | \core\common\Entity::L_OK => ['img' => 'Tabler/square-rounded-check-filled-green.svg', 'text' => _("OK")], |
||
481 | \core\common\Entity::L_REMARK => ['img' => 'Tabler/info-square-rounded-filled-blue.svg', 'text' => _("Remark")], |
||
482 | \core\common\Entity::L_WARN => ['img' => 'Tabler/alert-square-rounded-filled-yellow.svg', 'text' => _("Warning!")], |
||
483 | \core\common\Entity::L_ERROR => ['img' => 'Tabler/square-rounded-x-filled-red.svg', 'text' => _("Error!")], |
||
484 | \core\common\Entity::L_CERT_OK => ['img' => 'Tabler/certificate-green.svg', 'text' => _("OK")], |
||
485 | \core\common\Entity::L_CERT_WARN => ['img' => 'Tabler/certificate-red.svg', 'text' => _("Warning!")], |
||
486 | \core\common\Entity::L_CERT_ERROR => ['img' => 'Tabler/certificate-off.svg', 'text' => _("Warning!")], |
||
487 | ]; |
||
488 | |||
489 | $retval = ""; |
||
490 | if (!$omittabletags) { |
||
491 | $retval .= "<tr><td>"; |
||
492 | } |
||
493 | // $finalCaption = ($caption !== NULL ? $caption : $uiMessages[$level]['text']); |
||
494 | // $retval .= "<img class='icon cat-icon' src='" . $uiMessages[$level]['icon'] . "' alt='" . $finalCaption . "' title='" . $finalCaption . "'/>"; |
||
495 | $iconData = $uiMessages[$level]; |
||
496 | if ($caption !== NULL) { |
||
497 | $iconData['text'] = $caption; |
||
498 | } |
||
499 | |||
500 | |||
501 | $retval .= $this->catIcon($iconData); |
||
502 | |||
503 | if (!$omittabletags) { |
||
504 | $retval .= "</td><td>"; |
||
505 | } |
||
506 | if ($text !== NULL) { |
||
507 | $retval .= $text; |
||
508 | } |
||
509 | if (!$omittabletags) { |
||
510 | $retval .= "</td></tr>"; |
||
511 | } |
||
512 | \core\common\Entity::outOfThePotatoes(); |
||
513 | return $retval; |
||
514 | } |
||
515 | |||
516 | /** |
||
517 | * creates HTML code to display an "all is okay" message |
||
518 | * |
||
519 | * @param string $text the text to display |
||
520 | * @param string $caption the caption to display |
||
521 | * @param bool $omittabletags the output usually has tr/td table tags, this option suppresses them |
||
522 | * @return string HTML: the box |
||
523 | */ |
||
524 | public function boxOkay(string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) { |
||
525 | return $this->boxFlexible(\core\common\Entity::L_OK, $text, $caption, $omittabletags); |
||
526 | } |
||
527 | |||
528 | /** |
||
529 | * creates HTML code to display a "smartass comment" message |
||
530 | * |
||
531 | * @param string $text the text to display |
||
532 | * @param string $caption the caption to display |
||
533 | * @param bool $omittabletags the output usually has tr/td table tags, this option suppresses them |
||
534 | * @return string HTML: the box |
||
535 | */ |
||
536 | public function boxRemark(string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) { |
||
537 | return $this->boxFlexible(\core\common\Entity::L_REMARK, $text, $caption, $omittabletags); |
||
538 | } |
||
539 | |||
540 | /** |
||
541 | * creates HTML code to display a "something's a bit wrong" message |
||
542 | * |
||
543 | * @param string $text the text to display |
||
544 | * @param string $caption the caption to display |
||
545 | * @param bool $omittabletags the output usually has tr/td table tags, this option suppresses them |
||
546 | * @return string HTML: the box |
||
547 | */ |
||
548 | public function boxWarning(string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) { |
||
549 | return $this->boxFlexible(\core\common\Entity::L_WARN, $text, $caption, $omittabletags); |
||
550 | } |
||
551 | |||
552 | /** |
||
553 | * creates HTML code to display a "Whoa! Danger, Will Robinson!" message |
||
554 | * |
||
555 | * @param string $text the text to display |
||
556 | * @param string $caption the caption to display |
||
557 | * @param bool $omittabletags the output usually has tr/td table tags, this option suppresses them |
||
558 | * @return string HTML: the box |
||
559 | */ |
||
560 | public function boxError(string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) { |
||
561 | return $this->boxFlexible(\core\common\Entity::L_ERROR, $text, $caption, $omittabletags); |
||
562 | } |
||
563 | |||
564 | /** |
||
565 | * creates HTML code to display a "All fine" message |
||
566 | * |
||
567 | * @param string $text the text to display |
||
568 | * @param string $caption the caption to display |
||
569 | * @param bool $omittabletags the output usually has tr/td table tags, this option suppresses them |
||
570 | * @return string HTML: the box |
||
571 | */ |
||
572 | public function boxCertOK(string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) { |
||
573 | return $this->boxFlexible(\core\common\Entity::L_CERT_OK, $text, $caption, $omittabletags); |
||
574 | } |
||
575 | |||
576 | /** |
||
577 | * creates HTML code to display a "A certificate close to expiry" message |
||
578 | * |
||
579 | * @param string $text the text to display |
||
580 | * @param string $caption the caption to display |
||
581 | * @param bool $omittabletags the output usually has tr/td table tags, this option suppresses them |
||
582 | * @return string HTML: the box |
||
583 | */ |
||
584 | public function boxCertWarning(string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) { |
||
585 | return $this->boxFlexible(\core\common\Entity::L_CERT_WARN, $text, $caption, $omittabletags); |
||
586 | } |
||
587 | /** |
||
588 | * creates HTML code to display a "A certificate expired or dangerously close to expiry" message |
||
589 | * |
||
590 | * @param string $text the text to display |
||
591 | * @param string $caption the caption to display |
||
592 | * @param bool $omittabletags the output usually has tr/td table tags, this option suppresses them |
||
593 | * @return string HTML: the box |
||
594 | */ |
||
595 | public function boxCertError(string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) { |
||
596 | return $this->boxFlexible(\core\common\Entity::L_CERT_ERROR, $text, $caption, $omittabletags); |
||
597 | } |
||
598 | |||
599 | |||
600 | const QRCODE_PIXELS_PER_SYMBOL = 12; |
||
601 | |||
602 | /** |
||
603 | * Injects the consortium logo in the middle of a given PNG. |
||
604 | * |
||
605 | * Usually used on QR code PNGs - the parameters inform about the structure of |
||
606 | * the QR code so that the logo does not prevent parsing of the QR code. |
||
607 | * |
||
608 | * @param string $inputpngstring the PNG to edit |
||
609 | * @param int $symbolsize size in pixels of one QR "pixel" |
||
610 | * @param int $marginsymbols size in pixels of border around the actual QR |
||
611 | * @return string the image with logo centered in the middle |
||
612 | */ |
||
613 | public function pngInjectConsortiumLogo(string $inputpngstring, int $symbolsize, int $marginsymbols = 4) { |
||
614 | $loggerInstance = new \core\common\Logging(); |
||
615 | $inputgd = imagecreatefromstring($inputpngstring); |
||
616 | if ($inputgd === FALSE) { // source image is bogus; don't do anything |
||
617 | return ""; |
||
618 | } |
||
619 | |||
620 | $loggerInstance->debug(4, "Consortium logo is at: " . ROOT . "/web/resources/images/consortium_logo_large.png"); |
||
621 | $logogd = imagecreatefrompng(ROOT . "/web/resources/images/consortium_logo_large.png"); |
||
622 | if ($logogd === FALSE) { // consortium logo is bogus; don't do anything |
||
623 | return ""; |
||
624 | } |
||
625 | $sizeinput = [imagesx($inputgd), imagesy($inputgd)]; |
||
626 | $sizelogo = [imagesx($logogd), imagesy($logogd)]; |
||
627 | // Q level QR-codes can sustain 25% "damage" |
||
628 | // make our logo cover approx 15% of area to be sure; mind that there's a $symbolsize * $marginsymbols pixel white border around each edge |
||
629 | $totalpixels = ($sizeinput[0] - $symbolsize * $marginsymbols) * ($sizeinput[1] - $symbolsize * $marginsymbols); |
||
630 | $totallogopixels = ($sizelogo[0]) * ($sizelogo[1]); |
||
631 | $maxoccupy = $totalpixels * 0.04; |
||
632 | // find out how much we have to scale down logo to reach 10% QR estate |
||
633 | $scale = sqrt($maxoccupy / $totallogopixels); |
||
634 | $loggerInstance->debug(4, "Scaling info: $scale, $maxoccupy, $totallogopixels\n"); |
||
635 | // determine final pixel size - round to multitude of $symbolsize to match exact symbol boundary |
||
636 | $targetwidth = (int) ($symbolsize * round($sizelogo[0] * $scale / $symbolsize)); |
||
637 | $targetheight = (int) ($symbolsize * round($sizelogo[1] * $scale / $symbolsize)); |
||
638 | // paint white below the logo, in case it has transparencies (looks bad) |
||
639 | // have one symbol in each direction extra white space |
||
640 | $whiteimage = imagecreate($targetwidth + 2 * $symbolsize, $targetheight + 2 * $symbolsize); |
||
641 | if ($whiteimage === FALSE) { // we can't create an empty canvas. Weird. Stop processing. |
||
642 | return ""; |
||
643 | } |
||
644 | imagecolorallocate($whiteimage, 255, 255, 255); |
||
645 | // also make sure the initial placement is a multitude of 12; otherwise "two half" symbols might be affected |
||
646 | $targetplacementx = (int) ($symbolsize * round(($sizeinput[0] / 2 - ($targetwidth - $symbolsize + 1) / 2) / $symbolsize)); |
||
647 | $targetplacementy = (int) ($symbolsize * round(($sizeinput[1] / 2 - ($targetheight - $symbolsize + 1 ) / 2) / $symbolsize)); |
||
648 | imagecopyresized($inputgd, $whiteimage, $targetplacementx - $symbolsize, $targetplacementy - $symbolsize, 0, 0, $targetwidth + 2 * $symbolsize, $targetheight + 2 * $symbolsize, $targetwidth + 2 * $symbolsize, $targetheight + 2 * $symbolsize); |
||
649 | imagecopyresized($inputgd, $logogd, $targetplacementx, $targetplacementy, 0, 0, $targetwidth, $targetheight, $sizelogo[0], $sizelogo[1]); |
||
650 | ob_start(); |
||
651 | imagepng($inputgd); |
||
652 | return ob_get_clean(); |
||
653 | } |
||
654 | |||
655 | /** |
||
656 | * Something went wrong. We display the error cause and then throw an Exception. |
||
657 | * |
||
658 | * @param string $headerDisplay error to put in the page header |
||
659 | * @param string $uiDisplay error string to display |
||
660 | * @return void direct output |
||
661 | * @throws Exception |
||
662 | */ |
||
663 | public function errorPage($headerDisplay, $uiDisplay) { |
||
664 | $decoObject = new PageDecoration(); |
||
665 | echo $decoObject->pageheader($headerDisplay, "ADMIN-IDP"); |
||
666 | echo "<h1>$uiDisplay</h1>"; |
||
667 | echo $decoObject->footer(); |
||
668 | throw new Exception("Error page raised: $headerDisplay - $uiDisplay."); |
||
669 | } |
||
670 | |||
671 | /** |
||
672 | * creates the HTML code displaying the result of a test that was run previously |
||
673 | * |
||
674 | * @param \core\SanityTests $test the test that was run |
||
675 | * @return string |
||
676 | * @throws Exception |
||
677 | */ |
||
678 | public function sanityTestResultHTML($test) { |
||
679 | $out = ''; |
||
680 | switch ($test->test_result['global']) { |
||
681 | case \core\common\Entity::L_OK: |
||
682 | $message = "Your configuration appears to be fine."; |
||
683 | break; |
||
684 | case \core\common\Entity::L_WARN: |
||
685 | $message = "There were some warnings, but your configuration should work."; |
||
686 | break; |
||
687 | case \core\common\Entity::L_ERROR: |
||
688 | $message = "Your configuration appears to be broken, please fix the errors."; |
||
689 | if ($test->fatalError) { |
||
690 | $message .= "<br>Some of the errors prevented running additional tests so rerun after fixing."; |
||
691 | } |
||
692 | break; |
||
693 | case \core\common\Entity::L_REMARK: |
||
694 | $message = "Your configuration appears to be fine."; |
||
695 | break; |
||
696 | default: |
||
697 | throw new Exception("The result code level " . $test->test_result['global'] . " is not defined!"); |
||
698 | } |
||
699 | $out .= $this->boxFlexible($test->test_result['global'], "<br><strong>Test Summary</strong><br>" . $message . "<br>See below for details<br><hr>"); |
||
700 | foreach ($test->out as $testValue) { |
||
701 | foreach ($testValue as $o) { |
||
702 | $out .= $this->boxFlexible($o['level'], $o['message']); |
||
703 | } |
||
704 | } |
||
705 | return($out); |
||
706 | } |
||
707 | /** |
||
708 | * prepares data for icons |
||
709 | * |
||
710 | * @param string $index |
||
711 | * @return array |
||
712 | */ |
||
713 | public function iconData($index) { |
||
714 | \core\common\Entity::intoThePotatoes(); |
||
715 | $icons = [ |
||
716 | 'CERT_STATUS_OK' => ['img' => 'Tabler/certificate-green.svg', 'text' => _("All certificates are valid long enough")], |
||
717 | 'CERT_STATUS_WARN' => ['img' => 'Tabler/certificate-red.svg', 'text' => _("At least one certificate is close to expiry")], |
||
718 | 'CERT_STATUS_ERROR' => ['img' => 'Tabler/certificate-off.svg', 'text' => _("At least one certificate either has expired or is very close to expiry")], |
||
719 | 'OVERALL_OPENROAMING_LEVEL_GOOD' => ['img' => 'Tabler/square-rounded-check-green.svg', 'text' => _("OpenRoaming appears to be configured properly")], |
||
720 | 'OVERALL_OPENROAMING_LEVEL_NOTE' => ['img' => 'Tabler/info-square-rounded-blue.svg', 'text' => _("There are some minor OpenRoaming configuration issues")], |
||
721 | 'OVERALL_OPENROAMING_LEVEL_WARN' => ['img' => 'Tabler/info-square-rounded-blue.svg', 'text' => _("There are some average level OpenRoaming configuration issues")], |
||
722 | 'OVERALL_OPENROAMING_LEVEL_ERROR' => ['img' => 'Tabler/alert-square-rounded-red.svg', 'text' => _("There are some critical OpenRoaming configuration issues")], |
||
723 | 'PROFILES_SHOWTIME' => ['img' => 'Tabler/checks-green.svg', 'text' => _("At least one profile is fully configured and visible in the user interface")], |
||
724 | 'PROFILES_CONFIGURED' => ['img' => 'Tabler/check-green.svg', 'text' => _("At least one profile is fully configured but none are set as production-ready therefore the institution is not visible in the user interface")], |
||
725 | 'PROFILES_INCOMPLETE' => ['img' => 'Tabler/access-point-off-red.svg', 'text' => _("No configured profiles")], |
||
726 | 'PROFILES_REDIRECTED' => ['img' => 'Tabler/external-link.svg', 'text' => _("All active profiles redirected")], |
||
727 | 'IDP_LINKED' => ['img' => 'Tabler/database-green.svg', 'text' => _("Linked")], |
||
728 | 'IDP_NOT_LINKED' => ['img' => 'Tabler/database-off-red.svg', 'text' => _("NOT linked")], |
||
729 | 'CERTS_NOT_SHOWN' => ['img' => 'Tabler/question-mark-blue.svg', 'text' => _("Not showing cert info if no profiles are visible")], |
||
730 | ]; |
||
731 | \core\common\Entity::outOfThePotatoes(); |
||
732 | return($icons[$index]); |
||
733 | } |
||
734 | |||
735 | /** |
||
736 | * the HTML img element produced 0n the basis of a simple [src,title] array |
||
737 | * @param type array |
||
738 | * @return string the img element |
||
739 | */ |
||
740 | public function catIcon($data) { |
||
741 | return "<img src='../resources/images/icons/".$data['img']."' alt='".$data['text']."' title = '".$data['text']."' class='cat-icon'>"; |
||
742 | } |
||
743 | } |
||
744 |