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) { |
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 = [_("Support: Web") => "support:url", |
94
|
|
|
_("Support: EAP Types") => "support:eap_types", |
95
|
|
|
_("Support: Phone") => "support:phone", |
96
|
|
|
_("Support: E-Mail") => "support:email", |
97
|
|
|
sprintf(_("%s Name"), $this->nomenclatureParticipant) => "general:instname", |
98
|
|
|
sprintf(_("%s Acronym"), $this->nomenclatureParticipant) => "general:instshortname", |
99
|
|
|
_("Location") => "general:geo_coordinates", |
100
|
|
|
_("Logo URL") => "general:logo_url", |
101
|
|
|
_("Logo image") => "general:logo_file", |
102
|
|
|
_("Configure Wired Ethernet") => "media:wired", |
103
|
|
|
_("Name (CN) of Authentication Server") => "eap:server_name", |
104
|
|
|
_("Valid until") => "eap:ca_vailduntil", |
105
|
|
|
_("Enable device assessment") => "eap:enable_nea", |
106
|
|
|
_("Terms of Use") => "support:info_file", |
107
|
|
|
_("CA Certificate URL") => "eap:ca_url", |
108
|
|
|
_("CA Certificate File") => "eap:ca_file", |
109
|
|
|
_("Profile Display Name") => "profile:name", |
110
|
|
|
_("Production-Ready") => "profile:production", |
111
|
|
|
_("Admin Accepted IdP Terms of Use") => 'hiddenprofile:tou_accepted', |
112
|
|
|
_("Admin Accepted SP Terms of Use") => 'hiddenmanagedsp:tou_accepted', |
113
|
|
|
_("Extra text on downloadpage for device") => "device-specific:customtext", |
114
|
|
|
_("Redirection Target") => "device-specific:redirect", |
115
|
|
|
_("Extra text on downloadpage for EAP method") => "eap-specific:customtext", |
116
|
|
|
_("Turn on selection of EAP-TLS User-Name") => "eap-specific:tls_use_other_id", |
117
|
|
|
_("Use GEANTlink for TTLS (Windows 8 and 10)") => "device-specific:geantlink", |
118
|
|
|
_("Show the dedicated geteduroam download page for this device") => "device-specific:geteduroam", |
119
|
|
|
_("Profile Description") => "profile:description", |
120
|
|
|
_("Custom Installer Name Suffix") => "profile:customsuffix", |
121
|
|
|
_("OpenRoaming") => "media:openroaming", |
122
|
|
|
sprintf(_("%s Administrator"), $this->nomenclatureFed) => "user:fedadmin", |
123
|
|
|
_("Real Name") => "user:realname", |
124
|
|
|
_("E-Mail Address") => "user:email", |
125
|
|
|
_("Remove/Disable SSID") => "media:remove_SSID", |
126
|
|
|
_("Mandatory Content Filtering Proxy") => "media:force_proxy", |
127
|
|
|
_("Custom CSS file for User Area") => "fed:css_file", |
128
|
|
|
sprintf(_("%s Logo"), $this->nomenclatureFed) => "fed:logo_file", |
129
|
|
|
_("Preferred Skin for User Area") => "fed:desired_skin", |
130
|
|
|
sprintf(_("Include %s branding in installers"), $this->nomenclatureFed) => "fed:include_logo_installers", |
131
|
|
|
sprintf(_("%s Name"), $this->nomenclatureFed) => "fed:realname", |
132
|
|
|
sprintf(_("%s Homepage"), $this->nomenclatureFed) => "fed:url", |
133
|
|
|
sprintf(_("Custom text in %s Invitations"), $this->nomenclatureParticipant) => "fed:custominvite", |
134
|
|
|
sprintf(_("Enable %s"), \config\ConfAssistant::SILVERBULLET['product_name']) => "fed:silverbullet", |
135
|
|
|
sprintf(_("%s: Do not terminate EAP"), \core\ProfileSilverbullet::PRODUCTNAME) => "fed:silverbullet-noterm", |
136
|
|
|
sprintf(_("%s: max users per profile"), \core\ProfileSilverbullet::PRODUCTNAME) => "fed:silverbullet-maxusers", |
137
|
|
|
sprintf(_("Mint %s with CA on creation"), $this->nomenclatureIdP) => "fed:minted_ca_file", |
138
|
|
|
sprintf(_("OpenRoaming: Allow %s Opt-In"),$this->nomenclatureParticipant) => "fed:openroaming", |
139
|
|
|
_("OpenRoaming: Custom NAPTR Target") => "fed:openroaming_customtarget", |
140
|
|
|
$ssidText => "media:SSID", |
141
|
|
|
$passpointOiText => "media:consortium_OI", |
142
|
|
|
_("VLAN for own users") => "managedsp:vlan", |
143
|
|
|
_("Realm to be considered own users") => "managedsp:realmforvlan", |
144
|
|
|
_("Custom Operator-Name attribute") => "managedsp:operatorname", |
145
|
|
|
]; |
146
|
|
|
|
147
|
|
|
$find = array_keys($displayNames, $input, TRUE); |
148
|
|
|
|
149
|
|
|
if (count($find) == 0) { // this is an error! throw an Exception |
150
|
|
|
throw new \Exception("The translation of an option name was requested, but the option is not known to the system: " . htmlentities($input)); |
151
|
|
|
} |
152
|
|
|
\core\common\Entity::outOfThePotatoes(); |
153
|
|
|
// 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 |
154
|
|
|
return htmlspecialchars($find[0]); |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* creates an HTML information block with a list of options from a given category and level |
159
|
|
|
* @param array $optionlist list of options |
160
|
|
|
* @param string $class option class of interest |
161
|
|
|
* @param string $level option level of interest |
162
|
|
|
* @return string HTML code |
163
|
|
|
*/ |
164
|
|
|
public function infoblock(array $optionlist, string $class, string $level) { |
165
|
|
|
\core\common\Entity::intoThePotatoes(); |
166
|
|
|
$locationMarkers = []; |
167
|
|
|
$retval = ""; |
168
|
|
|
$optioninfo = \core\Options::instance(); |
169
|
|
|
|
170
|
|
|
foreach ($optionlist as $option) { |
171
|
|
|
$type = $optioninfo->optionType($option['name']); |
172
|
|
|
if (preg_match('/^' . $class . '/', $option['name']) && $option['level'] == "$level") { |
173
|
|
|
// all non-multilang attribs get this assignment ... |
174
|
|
|
$language = ""; |
175
|
|
|
$content = $option['value']; |
176
|
|
|
// ... override them with multilang tags if needed |
177
|
|
|
if ($type["flag"] == "ML") { |
178
|
|
|
$language = _("default/other languages"); |
179
|
|
|
if ($option['lang'] != 'C') { |
180
|
|
|
$language = \config\Master::LANGUAGES[$option['lang']]['display'] ?? "(unsupported language)"; |
181
|
|
|
} |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
switch ($type["type"]) { |
185
|
|
|
case "coordinates": |
186
|
|
|
$coords = json_decode($option['value'], true); |
187
|
|
|
$locationMarkers[] = $coords; |
188
|
|
|
break; |
189
|
|
|
case "file": |
190
|
|
|
$retval .= "<tr><td>" . $this->displayName($option['name']) . "</td><td>$language</td><td>"; |
191
|
|
|
switch ($option['name']) { |
192
|
|
|
case "general:logo_file": |
193
|
|
|
case "fed:logo_file": |
194
|
|
|
$retval .= $this->previewImageinHTML('ROWID-' . $option['level'] . '-' . $option['row_id']); |
195
|
|
|
break; |
196
|
|
|
case "eap:ca_file": |
197
|
|
|
// fall-through intended: display both the same way |
198
|
|
|
case "fed:minted_ca_file": |
199
|
|
|
$retval .= $this->previewCAinHTML('ROWID-' . $option['level'] . '-' . $option['row_id']); |
200
|
|
|
break; |
201
|
|
|
case "support:info_file": |
202
|
|
|
$retval .= $this->previewInfoFileinHTML('ROWID-' . $option['level'] . '-' . $option['row_id']); |
203
|
|
|
break; |
204
|
|
|
default: |
205
|
|
|
} |
206
|
|
|
break; |
207
|
|
|
case "boolean": |
208
|
|
|
if ($option['name'] == "fed:silverbullet" && \config\Master::FUNCTIONALITY_LOCATIONS['CONFASSISTANT_SILVERBULLET'] == "LOCAL" && \config\Master::FUNCTIONALITY_LOCATIONS['CONFASSISTANT_RADIUS'] != "LOCAL") { |
209
|
|
|
// do not display the option at all; it gets auto-set by the ProfileSilverbullet constructor and doesn't have to be seen |
210
|
|
|
break; |
211
|
|
|
} |
212
|
|
|
$retval .= "<tr><td>" . $this->displayName($option['name']) . "</td><td>$language</td><td><strong>" . ($content == "on" ? _("on") : _("off") ) . "</strong></td></tr>"; |
213
|
|
|
break; |
214
|
|
|
default: |
215
|
|
|
$retval .= "<tr><td>" . $this->displayName($option['name']) . "</td><td>$language</td><td><strong>$content</strong></td></tr>"; |
216
|
|
|
} |
217
|
|
|
} |
218
|
|
|
} |
219
|
|
|
if (count($locationMarkers)) { |
220
|
|
|
$marker = '<markers>'; |
221
|
|
|
$locationCount = 0; |
222
|
|
|
foreach ($locationMarkers as $g) { |
223
|
|
|
$locationCount++; |
224
|
|
|
$marker .= '<marker name="' . $locationCount . '" lat="' . $g['lat'] . '" lng="' . $g['lon'] . '" />'; |
225
|
|
|
} |
226
|
|
|
$marker .= '<\/markers>'; // some validator says this should be escaped |
227
|
|
|
$jMarker = json_encode($locationMarkers); |
228
|
|
|
$retval .= '<tr><td><script>markers=\'' . $marker . '\'; jmarkers = \'' . $jMarker . '\';</script></td><td></td><td></td></tr>'; |
229
|
|
|
} |
230
|
|
|
\core\common\Entity::outOfThePotatoes(); |
231
|
|
|
return $retval; |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
/** |
235
|
|
|
* creates HTML code to display all information boxes for an IdP |
236
|
|
|
* |
237
|
|
|
* @param \core\IdP $myInst the IdP in question |
238
|
|
|
* @return string HTML code |
239
|
|
|
*/ |
240
|
|
|
public function instLevelInfoBoxes(\core\IdP $myInst) { |
241
|
|
|
\core\common\Entity::intoThePotatoes(); |
242
|
|
|
$idpoptions = $myInst->getAttributes(); |
243
|
|
|
$retval = "<div class='infobox'> |
244
|
|
|
<h2>" . sprintf(_("General %s details"), $this->nomenclatureParticipant) . "</h2> |
245
|
|
|
<table> |
246
|
|
|
<tr> |
247
|
|
|
<td> |
248
|
|
|
" . _("Country:") . " |
249
|
|
|
</td> |
250
|
|
|
<td> |
251
|
|
|
</td> |
252
|
|
|
<td> |
253
|
|
|
<strong>"; |
254
|
|
|
$myFed = new \core\Federation($myInst->federation); |
255
|
|
|
$retval .= $myFed->name; |
256
|
|
|
$retval .= "</strong> |
257
|
|
|
</td> |
258
|
|
|
</tr>" . $this->infoblock($idpoptions, "general", "IdP") . " |
259
|
|
|
</table> |
260
|
|
|
</div>"; |
261
|
|
|
|
262
|
|
|
$blocks = [["support", _("Global Helpdesk Details")], ["media", _("Media Properties")]]; |
263
|
|
|
foreach ($blocks as $block) { |
264
|
|
|
$retval .= "<div class='infobox'> |
265
|
|
|
<h2>" . $block[1] . "</h2> |
266
|
|
|
<table>" . |
267
|
|
|
$this->infoblock($idpoptions, $block[0], "IdP") . |
268
|
|
|
"</table> |
269
|
|
|
</div>"; |
270
|
|
|
} |
271
|
|
|
\core\common\Entity::outOfThePotatoes(); |
272
|
|
|
return $retval; |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
/** |
276
|
|
|
* pretty-prints a file size number in SI "bi" units |
277
|
|
|
* @param int $number the size of the file |
278
|
|
|
* @return string the pretty-print representation of the file size |
279
|
|
|
*/ |
280
|
|
|
private function displaySize(int $number) { |
281
|
|
|
if ($number > 1024 * 1024) { |
282
|
|
|
return round($number / 1024 / 1024, 2) . " MiB"; |
283
|
|
|
} |
284
|
|
|
if ($number > 1024) { |
285
|
|
|
return round($number / 1024, 2) . " KiB"; |
286
|
|
|
} |
287
|
|
|
return $number . " B"; |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
/** |
291
|
|
|
* |
292
|
|
|
* @param string $table the database table |
293
|
|
|
* @param integer $rowindex the database row_id |
294
|
|
|
* @param boolean $checkpublic should we check if the requested piece of data is public? |
295
|
|
|
* @return string|boolean the requested data, or FALSE if something went wrong |
296
|
|
|
*/ |
297
|
|
|
public static function getBlobFromDB($table, $rowindex, $checkpublic) { |
298
|
|
|
// the data is either public (just give it away) or not; in this case, only |
299
|
|
|
// release if the data belongs to admin himself |
300
|
|
|
if ($checkpublic) { |
301
|
|
|
|
302
|
|
|
$owners = \core\EntityWithDBProperties::isDataRestricted($table, $rowindex); |
303
|
|
|
|
304
|
|
|
$ownersCondensed = []; |
305
|
|
|
|
306
|
|
|
if ($owners !== FALSE) { // restricted data, see if we're authenticated and owners of the data |
307
|
|
|
$auth = new \web\lib\admin\Authentication(); |
308
|
|
|
if (!$auth->isAuthenticated()) { |
309
|
|
|
return FALSE; // admin-only, but we are not an admin |
310
|
|
|
} |
311
|
|
|
// we might be called without session context (filepreview) so get the |
312
|
|
|
// context if needed |
313
|
|
|
\core\CAT::sessionStart(); |
314
|
|
|
|
315
|
|
|
foreach ($owners as $oneowner) { |
316
|
|
|
$ownersCondensed[] = $oneowner['ID']; |
317
|
|
|
} |
318
|
|
|
if (array_search($_SESSION['user'], $ownersCondensed) === FALSE) { |
319
|
|
|
return FALSE; // wrong guy |
320
|
|
|
} |
321
|
|
|
// carry on and get the data |
322
|
|
|
} |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
$blob = \core\EntityWithDBProperties::fetchRawDataByIndex($table, $rowindex); |
326
|
|
|
return $blob; // this means we might return FALSE here if something was wrong with the original requested reference |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
/** |
330
|
|
|
* creates HTML code to display a nice UI representation of a CA |
331
|
|
|
* |
332
|
|
|
* @param string $cAReference ROWID pointer to the CA to display |
333
|
|
|
* @return string HTML code |
334
|
|
|
*/ |
335
|
|
|
public function previewCAinHTML($cAReference) { |
336
|
|
|
\core\common\Entity::intoThePotatoes(); |
337
|
|
|
$validator = new \web\lib\common\InputValidation(); |
338
|
|
|
$ref = $validator->databaseReference($cAReference); |
339
|
|
|
$caExpiryTrashhold = \config\ConfAssistant::CERT_WARNINGS['expiry_warning']; |
340
|
|
|
$rawResult = UIElements::getBlobFromDB($ref['table'], $ref['rowindex'], FALSE); |
341
|
|
|
if (is_bool($rawResult)) { // we didn't actually get a CA! |
342
|
|
|
$retval = "<div class='ca-summary'>" . _("There was an error while retrieving the certificate from the database!") . "</div>"; |
343
|
|
|
\core\common\Entity::outOfThePotatoes(); |
344
|
|
|
return $retval; |
345
|
|
|
} |
346
|
|
|
$cAblob = base64_decode($rawResult); |
347
|
|
|
|
348
|
|
|
$func = new \core\common\X509; |
349
|
|
|
$details = $func->processCertificate($cAblob); |
350
|
|
|
if ($details === FALSE) { |
351
|
|
|
$retval = _("There was an error processing the certificate!"); |
352
|
|
|
\core\common\Entity::outOfThePotatoes(); |
353
|
|
|
return $retval; |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
$details['name'] = preg_replace('/(.)\/(.)/', "$1<br/>$2", $details['name']); |
357
|
|
|
$details['name'] = preg_replace('/\//', "", $details['name']); |
358
|
|
|
$certstatus = ( $details['root'] == 1 ? "R" : "I"); |
359
|
|
|
$certTooltip = ( $details['root'] == 1 ? _("Root CA") : _("Intermediate CA")); |
360
|
|
|
$innerbgColor = "#0000ff"; |
361
|
|
|
$leftBorderColor = "#00ff00"; |
362
|
|
|
$message = ""; |
363
|
|
|
if ($details['ca'] == 0 && $details['root'] != 1) { |
364
|
|
|
$leftBorderColor = "red"; |
365
|
|
|
$message = _("This is a <strong>SERVER</strong> certificate!"); |
366
|
|
|
if (\config\ConfAssistant::CERT_GUIDELINES !== '') { |
367
|
|
|
$message .= "<br/><a target='_blank' href='".\config\ConfAssistant::CERT_GUIDELINES."'>". _("more info")."</a>"; |
368
|
|
|
} |
369
|
|
|
$message .= "<br/>"; |
370
|
|
|
$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>"; |
371
|
|
|
\core\common\Entity::outOfThePotatoes(); |
372
|
|
|
return $retval; |
373
|
|
|
} |
374
|
|
|
$now = time(); |
375
|
|
|
if ($now + \config\ConfAssistant::CERT_WARNINGS['expiry_critical'] > $details['full_details']['validTo_time_t']) { |
376
|
|
|
$leftBorderColor = "red"; |
377
|
|
|
$message = _("Certificate expired!") . "<br>"; |
378
|
|
|
} elseif($now + \config\ConfAssistant::CERT_WARNINGS['expiry_warning'] > $details['full_details']['validTo_time_t'] - $caExpiryTrashhold) { |
379
|
|
|
if ($leftBorderColor == "#00ff00") { |
380
|
|
|
$leftBorderColor = "yellow"; |
381
|
|
|
} |
382
|
|
|
$message = _("Certificate close to expiry!") . "<br/>"; |
383
|
|
|
} |
384
|
|
|
|
385
|
|
|
if ($details['root'] == 1 && $details['basicconstraints_set'] == 0) { |
386
|
|
|
if ($leftBorderColor == "#00ff00") { |
387
|
|
|
$leftBorderColor = "yellow"; |
388
|
|
|
} |
389
|
|
|
$message .= "<div style='max-width: 25em'><strong>" . _("Improper root certificate, required critical CA extension missing, will not reliably install!") . "</strong>"; |
390
|
|
|
if (\config\ConfAssistant::CERT_GUIDELINES !== '') { |
391
|
|
|
$message .= "<br/><a target='_blank' href='".\config\ConfAssistant::CERT_GUIDELINES."'>". _("more info")."</a>"; |
392
|
|
|
} |
393
|
|
|
$message .= "</div><br/>"; |
394
|
|
|
} |
395
|
|
|
$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>"; |
396
|
|
|
\core\common\Entity::outOfThePotatoes(); |
397
|
|
|
return $retval; |
398
|
|
|
} |
399
|
|
|
|
400
|
|
|
/** |
401
|
|
|
* creates HTML code to display a nice UI representation of an image |
402
|
|
|
* |
403
|
|
|
* @param string $imageReference ROWID pointer to the image to display |
404
|
|
|
* @return string HTML code |
405
|
|
|
*/ |
406
|
|
|
public function previewImageinHTML($imageReference) { |
407
|
|
|
\core\common\Entity::intoThePotatoes(); |
408
|
|
|
$retval = "<img style='max-width:150px' src='inc/filepreview.php?id=" . $imageReference . "' alt='" . _("Preview of logo file") . "'/>"; |
409
|
|
|
\core\common\Entity::outOfThePotatoes(); |
410
|
|
|
return $retval; |
411
|
|
|
} |
412
|
|
|
|
413
|
|
|
/** |
414
|
|
|
* creates HTML code to display a nice UI representation of a TermsOfUse file |
415
|
|
|
* |
416
|
|
|
* @param string $fileReference ROWID pointer to the file to display |
417
|
|
|
* @return string HTML code |
418
|
|
|
*/ |
419
|
|
|
public function previewInfoFileinHTML($fileReference) { |
420
|
|
|
\core\common\Entity::intoThePotatoes(); |
421
|
|
|
$validator = new \web\lib\common\InputValidation(); |
422
|
|
|
$ref = $validator->databaseReference($fileReference); |
423
|
|
|
$fileBlob = UIElements::getBlobFromDB($ref['table'], $ref['rowindex'], FALSE); |
424
|
|
|
if (is_bool($fileBlob)) { // we didn't actually get a file! |
425
|
|
|
$retval = "<div class='ca-summary'>" . _("There was an error while retrieving the file from the database!") . "</div>"; |
426
|
|
|
\core\common\Entity::outOfThePotatoes(); |
427
|
|
|
return $retval; |
428
|
|
|
} |
429
|
|
|
$decodedFileBlob = base64_decode($fileBlob); |
430
|
|
|
$fileinfo = new \finfo(); |
431
|
|
|
$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>"; |
432
|
|
|
\core\common\Entity::outOfThePotatoes(); |
433
|
|
|
return $retval; |
434
|
|
|
} |
435
|
|
|
|
436
|
|
|
/** |
437
|
|
|
* creates HTML code for a UI element which informs the user about something. |
438
|
|
|
* |
439
|
|
|
* @param int $level what kind of information is to be displayed? |
440
|
|
|
* @param string $text the text to display |
441
|
|
|
* @param string $caption the caption to display |
442
|
|
|
* @param bool $omittabletags the output usually has tr/td table tags, this option suppresses them |
443
|
|
|
* @return string |
444
|
|
|
*/ |
445
|
|
|
public function boxFlexible(int $level, string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) { |
446
|
|
|
\core\common\Entity::intoThePotatoes(); |
447
|
|
|
$uiMessages = [ |
448
|
|
|
\core\common\Entity::L_OK => ['img' => 'Tabler/square-rounded-check-filled-green.svg', 'text' => _("OK")], |
449
|
|
|
\core\common\Entity::L_REMARK => ['img' => 'Tabler/info-square-rounded-filled-blue.svg', 'text' => _("Remark")], |
450
|
|
|
\core\common\Entity::L_WARN => ['img' => 'Tabler/alert-square-rounded-filled-yellow.svg', 'text' => _("Warning!")], |
451
|
|
|
\core\common\Entity::L_ERROR => ['img' => 'Tabler/square-rounded-x-filled-red.svg', 'text' => _("Error!")], |
452
|
|
|
\core\common\Entity::L_CERT_OK => ['img' => 'Tabler/certificate-green.svg', 'text' => _("OK")], |
453
|
|
|
\core\common\Entity::L_CERT_WARN => ['img' => 'Tabler/certificate-red.svg', 'text' => _("Warning!")], |
454
|
|
|
\core\common\Entity::L_CERT_ERROR => ['img' => 'Tabler/certificate-off.svg', 'text' => _("Warning!")], |
455
|
|
|
]; |
456
|
|
|
|
457
|
|
|
$retval = ""; |
458
|
|
|
if (!$omittabletags) { |
459
|
|
|
$retval .= "<tr><td>"; |
460
|
|
|
} |
461
|
|
|
// $finalCaption = ($caption !== NULL ? $caption : $uiMessages[$level]['text']); |
462
|
|
|
// $retval .= "<img class='icon cat-icon' src='" . $uiMessages[$level]['icon'] . "' alt='" . $finalCaption . "' title='" . $finalCaption . "'/>"; |
463
|
|
|
$iconData = $uiMessages[$level]; |
464
|
|
|
if ($caption !== NULL) { |
465
|
|
|
$iconData['text'] = $caption; |
466
|
|
|
} |
467
|
|
|
|
468
|
|
|
|
469
|
|
|
$retval .= $this->catIcon($iconData); |
470
|
|
|
|
471
|
|
|
if (!$omittabletags) { |
472
|
|
|
$retval .= "</td><td>"; |
473
|
|
|
} |
474
|
|
|
if ($text !== NULL) { |
475
|
|
|
$retval .= $text; |
476
|
|
|
} |
477
|
|
|
if (!$omittabletags) { |
478
|
|
|
$retval .= "</td></tr>"; |
479
|
|
|
} |
480
|
|
|
\core\common\Entity::outOfThePotatoes(); |
481
|
|
|
return $retval; |
482
|
|
|
} |
483
|
|
|
|
484
|
|
|
/** |
485
|
|
|
* creates HTML code to display an "all is okay" message |
486
|
|
|
* |
487
|
|
|
* @param string $text the text to display |
488
|
|
|
* @param string $caption the caption to display |
489
|
|
|
* @param bool $omittabletags the output usually has tr/td table tags, this option suppresses them |
490
|
|
|
* @return string HTML: the box |
491
|
|
|
*/ |
492
|
|
|
public function boxOkay(string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) { |
493
|
|
|
return $this->boxFlexible(\core\common\Entity::L_OK, $text, $caption, $omittabletags); |
494
|
|
|
} |
495
|
|
|
|
496
|
|
|
/** |
497
|
|
|
* creates HTML code to display a "smartass comment" message |
498
|
|
|
* |
499
|
|
|
* @param string $text the text to display |
500
|
|
|
* @param string $caption the caption to display |
501
|
|
|
* @param bool $omittabletags the output usually has tr/td table tags, this option suppresses them |
502
|
|
|
* @return string HTML: the box |
503
|
|
|
*/ |
504
|
|
|
public function boxRemark(string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) { |
505
|
|
|
return $this->boxFlexible(\core\common\Entity::L_REMARK, $text, $caption, $omittabletags); |
506
|
|
|
} |
507
|
|
|
|
508
|
|
|
/** |
509
|
|
|
* creates HTML code to display a "something's a bit wrong" message |
510
|
|
|
* |
511
|
|
|
* @param string $text the text to display |
512
|
|
|
* @param string $caption the caption to display |
513
|
|
|
* @param bool $omittabletags the output usually has tr/td table tags, this option suppresses them |
514
|
|
|
* @return string HTML: the box |
515
|
|
|
*/ |
516
|
|
|
public function boxWarning(string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) { |
517
|
|
|
return $this->boxFlexible(\core\common\Entity::L_WARN, $text, $caption, $omittabletags); |
518
|
|
|
} |
519
|
|
|
|
520
|
|
|
/** |
521
|
|
|
* creates HTML code to display a "Whoa! Danger, Will Robinson!" message |
522
|
|
|
* |
523
|
|
|
* @param string $text the text to display |
524
|
|
|
* @param string $caption the caption to display |
525
|
|
|
* @param bool $omittabletags the output usually has tr/td table tags, this option suppresses them |
526
|
|
|
* @return string HTML: the box |
527
|
|
|
*/ |
528
|
|
|
public function boxError(string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) { |
529
|
|
|
return $this->boxFlexible(\core\common\Entity::L_ERROR, $text, $caption, $omittabletags); |
530
|
|
|
} |
531
|
|
|
|
532
|
|
|
/** |
533
|
|
|
* creates HTML code to display a "All fine" message |
534
|
|
|
* |
535
|
|
|
* @param string $text the text to display |
536
|
|
|
* @param string $caption the caption to display |
537
|
|
|
* @param bool $omittabletags the output usually has tr/td table tags, this option suppresses them |
538
|
|
|
* @return string HTML: the box |
539
|
|
|
*/ |
540
|
|
|
public function boxCertOK(string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) { |
541
|
|
|
return $this->boxFlexible(\core\common\Entity::L_CERT_OK, $text, $caption, $omittabletags); |
542
|
|
|
} |
543
|
|
|
|
544
|
|
|
/** |
545
|
|
|
* creates HTML code to display a "A certificate close to expiry" message |
546
|
|
|
* |
547
|
|
|
* @param string $text the text to display |
548
|
|
|
* @param string $caption the caption to display |
549
|
|
|
* @param bool $omittabletags the output usually has tr/td table tags, this option suppresses them |
550
|
|
|
* @return string HTML: the box |
551
|
|
|
*/ |
552
|
|
|
public function boxCertWarning(string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) { |
553
|
|
|
return $this->boxFlexible(\core\common\Entity::L_CERT_WARN, $text, $caption, $omittabletags); |
554
|
|
|
} |
555
|
|
|
/** |
556
|
|
|
* creates HTML code to display a "A certificate expired or dangerously close to expiry" message |
557
|
|
|
* |
558
|
|
|
* @param string $text the text to display |
559
|
|
|
* @param string $caption the caption to display |
560
|
|
|
* @param bool $omittabletags the output usually has tr/td table tags, this option suppresses them |
561
|
|
|
* @return string HTML: the box |
562
|
|
|
*/ |
563
|
|
|
public function boxCertError(string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) { |
564
|
|
|
return $this->boxFlexible(\core\common\Entity::L_CERT_ERROR, $text, $caption, $omittabletags); |
565
|
|
|
} |
566
|
|
|
|
567
|
|
|
|
568
|
|
|
const QRCODE_PIXELS_PER_SYMBOL = 12; |
569
|
|
|
|
570
|
|
|
/** |
571
|
|
|
* Injects the consortium logo in the middle of a given PNG. |
572
|
|
|
* |
573
|
|
|
* Usually used on QR code PNGs - the parameters inform about the structure of |
574
|
|
|
* the QR code so that the logo does not prevent parsing of the QR code. |
575
|
|
|
* |
576
|
|
|
* @param string $inputpngstring the PNG to edit |
577
|
|
|
* @param int $symbolsize size in pixels of one QR "pixel" |
578
|
|
|
* @param int $marginsymbols size in pixels of border around the actual QR |
579
|
|
|
* @return string the image with logo centered in the middle |
580
|
|
|
*/ |
581
|
|
|
public function pngInjectConsortiumLogo(string $inputpngstring, int $symbolsize, int $marginsymbols = 4) { |
582
|
|
|
$loggerInstance = new \core\common\Logging(); |
583
|
|
|
$inputgd = imagecreatefromstring($inputpngstring); |
584
|
|
|
if ($inputgd === FALSE) { // source image is bogus; don't do anything |
585
|
|
|
return ""; |
586
|
|
|
} |
587
|
|
|
|
588
|
|
|
$loggerInstance->debug(4, "Consortium logo is at: " . ROOT . "/web/resources/images/consortium_logo_large.png"); |
589
|
|
|
$logogd = imagecreatefrompng(ROOT . "/web/resources/images/consortium_logo_large.png"); |
590
|
|
|
if ($logogd === FALSE) { // consortium logo is bogus; don't do anything |
591
|
|
|
return ""; |
592
|
|
|
} |
593
|
|
|
$sizeinput = [imagesx($inputgd), imagesy($inputgd)]; |
594
|
|
|
$sizelogo = [imagesx($logogd), imagesy($logogd)]; |
595
|
|
|
// Q level QR-codes can sustain 25% "damage" |
596
|
|
|
// make our logo cover approx 15% of area to be sure; mind that there's a $symbolsize * $marginsymbols pixel white border around each edge |
597
|
|
|
$totalpixels = ($sizeinput[0] - $symbolsize * $marginsymbols) * ($sizeinput[1] - $symbolsize * $marginsymbols); |
598
|
|
|
$totallogopixels = ($sizelogo[0]) * ($sizelogo[1]); |
599
|
|
|
$maxoccupy = $totalpixels * 0.04; |
600
|
|
|
// find out how much we have to scale down logo to reach 10% QR estate |
601
|
|
|
$scale = sqrt($maxoccupy / $totallogopixels); |
602
|
|
|
$loggerInstance->debug(4, "Scaling info: $scale, $maxoccupy, $totallogopixels\n"); |
603
|
|
|
// determine final pixel size - round to multitude of $symbolsize to match exact symbol boundary |
604
|
|
|
$targetwidth = (int) ($symbolsize * round($sizelogo[0] * $scale / $symbolsize)); |
605
|
|
|
$targetheight = (int) ($symbolsize * round($sizelogo[1] * $scale / $symbolsize)); |
606
|
|
|
// paint white below the logo, in case it has transparencies (looks bad) |
607
|
|
|
// have one symbol in each direction extra white space |
608
|
|
|
$whiteimage = imagecreate($targetwidth + 2 * $symbolsize, $targetheight + 2 * $symbolsize); |
609
|
|
|
if ($whiteimage === FALSE) { // we can't create an empty canvas. Weird. Stop processing. |
610
|
|
|
return ""; |
611
|
|
|
} |
612
|
|
|
imagecolorallocate($whiteimage, 255, 255, 255); |
613
|
|
|
// also make sure the initial placement is a multitude of 12; otherwise "two half" symbols might be affected |
614
|
|
|
$targetplacementx = (int) ($symbolsize * round(($sizeinput[0] / 2 - ($targetwidth - $symbolsize + 1) / 2) / $symbolsize)); |
615
|
|
|
$targetplacementy = (int) ($symbolsize * round(($sizeinput[1] / 2 - ($targetheight - $symbolsize + 1 ) / 2) / $symbolsize)); |
616
|
|
|
imagecopyresized($inputgd, $whiteimage, $targetplacementx - $symbolsize, $targetplacementy - $symbolsize, 0, 0, $targetwidth + 2 * $symbolsize, $targetheight + 2 * $symbolsize, $targetwidth + 2 * $symbolsize, $targetheight + 2 * $symbolsize); |
617
|
|
|
imagecopyresized($inputgd, $logogd, $targetplacementx, $targetplacementy, 0, 0, $targetwidth, $targetheight, $sizelogo[0], $sizelogo[1]); |
618
|
|
|
ob_start(); |
619
|
|
|
imagepng($inputgd); |
620
|
|
|
return ob_get_clean(); |
621
|
|
|
} |
622
|
|
|
|
623
|
|
|
/** |
624
|
|
|
* Something went wrong. We display the error cause and then throw an Exception. |
625
|
|
|
* |
626
|
|
|
* @param string $headerDisplay error to put in the page header |
627
|
|
|
* @param string $uiDisplay error string to display |
628
|
|
|
* @return void direct output |
629
|
|
|
* @throws Exception |
630
|
|
|
*/ |
631
|
|
|
public function errorPage($headerDisplay, $uiDisplay) { |
632
|
|
|
$decoObject = new PageDecoration(); |
633
|
|
|
echo $decoObject->pageheader($headerDisplay, "ADMIN-IDP"); |
634
|
|
|
echo "<h1>$uiDisplay</h1>"; |
635
|
|
|
echo $decoObject->footer(); |
636
|
|
|
throw new Exception("Error page raised: $headerDisplay - $uiDisplay."); |
637
|
|
|
} |
638
|
|
|
|
639
|
|
|
/** |
640
|
|
|
* creates the HTML code displaying the result of a test that was run previously |
641
|
|
|
* |
642
|
|
|
* @param \core\SanityTests $test the test that was run |
643
|
|
|
* @return string |
644
|
|
|
* @throws Exception |
645
|
|
|
*/ |
646
|
|
|
public function sanityTestResultHTML($test) { |
647
|
|
|
$out = ''; |
648
|
|
|
switch ($test->test_result['global']) { |
649
|
|
|
case \core\common\Entity::L_OK: |
650
|
|
|
$message = "Your configuration appears to be fine."; |
651
|
|
|
break; |
652
|
|
|
case \core\common\Entity::L_WARN: |
653
|
|
|
$message = "There were some warnings, but your configuration should work."; |
654
|
|
|
break; |
655
|
|
|
case \core\common\Entity::L_ERROR: |
656
|
|
|
$message = "Your configuration appears to be broken, please fix the errors."; |
657
|
|
|
if ($test->fatalError) { |
658
|
|
|
$message .= "<br>Some of the errors prevented running additional tests so rerun after fixing."; |
659
|
|
|
} |
660
|
|
|
break; |
661
|
|
|
case \core\common\Entity::L_REMARK: |
662
|
|
|
$message = "Your configuration appears to be fine."; |
663
|
|
|
break; |
664
|
|
|
default: |
665
|
|
|
throw new Exception("The result code level " . $test->test_result['global'] . " is not defined!"); |
666
|
|
|
} |
667
|
|
|
$out .= $this->boxFlexible($test->test_result['global'], "<br><strong>Test Summary</strong><br>" . $message . "<br>See below for details<br><hr>"); |
668
|
|
|
foreach ($test->out as $testValue) { |
669
|
|
|
foreach ($testValue as $o) { |
670
|
|
|
$out .= $this->boxFlexible($o['level'], $o['message']); |
671
|
|
|
} |
672
|
|
|
} |
673
|
|
|
return($out); |
674
|
|
|
} |
675
|
|
|
/** |
676
|
|
|
* prepares data for icons |
677
|
|
|
* |
678
|
|
|
* @param string $index |
679
|
|
|
* @return array |
680
|
|
|
*/ |
681
|
|
|
public function iconData($index) { |
682
|
|
|
\core\common\Entity::intoThePotatoes(); |
683
|
|
|
$icons = [ |
684
|
|
|
'CERT_STATUS_OK' => ['img' => 'Tabler/certificate-green.svg', 'text' => _("All certificates are valid long enough")], |
685
|
|
|
'CERT_STATUS_WARN' => ['img' => 'Tabler/certificate-red.svg', 'text' => _("At least one certificate is close to expiry")], |
686
|
|
|
'CERT_STATUS_ERROR' => ['img' => 'Tabler/certificate-off.svg', 'text' => _("At least one certificate either has expired or is very close to expiry")], |
687
|
|
|
'OVERALL_OPENROAMING_LEVEL_GOOD' => ['img' => 'Tabler/square-rounded-check-green.svg', 'text' => _("OpenRoaming appears to be configured properly")], |
688
|
|
|
'OVERALL_OPENROAMING_LEVEL_NOTE' => ['img' => 'Tabler/info-square-rounded-blue.svg', 'text' => _("There are some minor OpenRoaming configuration issues")], |
689
|
|
|
'OVERALL_OPENROAMING_LEVEL_WARN' => ['img' => 'Tabler/info-square-rounded-blue.svg', 'text' => _("There are some avarage level OpenRoaming configuration issues")], |
690
|
|
|
'OVERALL_OPENROAMING_LEVEL_ERROR' => ['img' => 'Tabler/alert-square-rounded-red.svg', 'text' => _("There are some critical OpenRoaming configuration issues")], |
691
|
|
|
'PROFILES_SHOWTIME' => ['img' => 'Tabler/checks-green.svg', 'text' => _("At least one profile is fully configured and visible in the user interface")], |
692
|
|
|
'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")], |
693
|
|
|
'PROFILES_INCOMPLETE' => ['img' => 'Tabler/access-point-off-red.svg', 'text' => _("No configured profiles")], |
694
|
|
|
'PROFILES_REDIRECTED' => ['img' => 'Tabler/external-link.svg', 'text' => _("All active profiles redirected")], |
695
|
|
|
'IDP_LINKED' => ['img' => 'Tabler/database-green.svg', 'text' => _("Linked")], |
696
|
|
|
'IDP_NOT_LINKED' => ['img' => 'Tabler/database-off-red.svg', 'text' => _("NOT linked")], |
697
|
|
|
'CERTS_NOT_SHOWN' => ['img' => 'Tabler/question-mark-blue.svg', 'text' => _("Not showing cert info if no profiles are visible")], |
698
|
|
|
]; |
699
|
|
|
\core\common\Entity::outOfThePotatoes(); |
700
|
|
|
return($icons[$index]); |
701
|
|
|
} |
702
|
|
|
|
703
|
|
|
/** |
704
|
|
|
* the HTML img element produced 0n the basis of a simple [src,title] array |
705
|
|
|
* @param type array |
706
|
|
|
* @return string the img element |
707
|
|
|
*/ |
708
|
|
|
public function catIcon($data) { |
709
|
|
|
return "<img src='../resources/images/icons/".$data['img']."' alt='".$data['text']."' title = '".$data['text']."' class='cat-icon'>"; |
710
|
|
|
} |
711
|
|
|
} |
712
|
|
|
|