Issues (173)

Security Analysis    13 potential vulnerabilities

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  Response Splitting (3)
Response Splitting can be used to send arbitrary responses.
  File Manipulation (6)
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Cross-Site Scripting (1)
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

web/lib/admin/UIElements.php (1 issue)

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
The expression return array('display' =...yNames[$input]['help']) returns the type array<string,string> which is incompatible with the documented return type string.
Loading history...
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