UIElements::previewInfoFileinHTML()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

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