From 0722bc91381a1c3bcd31c8bcaf40a382b8196239 Mon Sep 17 00:00:00 2001 From: Tomasz Date: Wed, 24 Apr 2024 11:57:44 +0000 Subject: [PATCH] changes required for read-only support for admin pages --- core/AbstractProfile.php | 54 +++-- core/Federation.php | 3 + core/IdP.php | 6 + core/User.php | 14 +- web/admin/edit_federation.php | 20 +- web/admin/js/nro.js | 86 ++++--- web/admin/overview_federation.php | 215 ++++++++++-------- web/admin/overview_org.php | 27 ++- web/lib/admin/UIElements.php | 66 +++++- web/lib/common/InputValidation.php | 41 +++- web/resources/css/cat.css.php | 9 +- web/resources/images/icons/LICENSE | 2 + .../icons/Tabler/access-point-off-red.svg | 1 + .../images/icons/Tabler/certificate-green.svg | 2 +- .../images/icons/Tabler/check-green.svg | 2 +- .../images/icons/Tabler/checks-green.svg | 2 +- .../images/icons/Tabler/database-green.svg | 1 + .../images/icons/Tabler/database-off-red.svg | 1 + .../icons/Tabler/question-mark-blue.svg | 1 + .../Tabler/square-rounded-check-green.svg | 2 +- 20 files changed, 380 insertions(+), 175 deletions(-) create mode 100644 web/resources/images/icons/Tabler/access-point-off-red.svg create mode 100644 web/resources/images/icons/Tabler/database-green.svg create mode 100644 web/resources/images/icons/Tabler/database-off-red.svg create mode 100644 web/resources/images/icons/Tabler/question-mark-blue.svg diff --git a/core/AbstractProfile.php b/core/AbstractProfile.php index e3b400129..f0a117e6f 100644 --- a/core/AbstractProfile.php +++ b/core/AbstractProfile.php @@ -130,21 +130,47 @@ abstract class AbstractProfile extends EntityWithDBProperties const OVERALL_OPENROAMING_LEVEL_WARN = 1; const OVERALL_OPENROAMING_LEVEL_ERROR = 0; - +/** + * constants used for displaying messages + */ const OPENROAMING_ALL_GOOD = 24; - const OPENROAMING_NO_REALM = 17; //n - const OPENROAMING_BAD_SRV = 16; //n - const OPENROAMING_BAD_NAPTR = 10; // w - const OPENROAMING_SOME_BAD_CONNECTIONS = 8; //w - const OPENROAMING_NO_DNSSEC = 8; //w - const OPENROAMING_NO_NAPTR = 3; //e - const OPENROAMING_BAD_NAPTR_RESOLVE = 2; //e - const OPENROAMING_BAD_SRV_RESOLVE = 1; //e - const OPENROAMING_BAD_CONNECTION = 0; //e + const OPENROAMING_NO_REALM = 17; //none + const OPENROAMING_BAD_SRV = 16; //none + const OPENROAMING_BAD_NAPTR = 10; // warning + const OPENROAMING_SOME_BAD_CONNECTIONS = 8; //warning + const OPENROAMING_NO_DNSSEC = 8; //warning + const OPENROAMING_NO_NAPTR = 3; //error + const OPENROAMING_BAD_NAPTR_RESOLVE = 2; //error + const OPENROAMING_BAD_SRV_RESOLVE = 1; //error + const OPENROAMING_BAD_CONNECTION = 0; //error + + const READINESS_LEVEL_NOTREADY = 0; + const READINESS_LEVEL_SUFFICIENTCONFIG = 1; + const READINESS_LEVEL_SHOWTIME = 2; + const CERT_STATUS_OK = 0; + const CERT_STATUS_WARN = 1; + const CERT_STATUS_ERROR = 2; + const OVERALL_OPENROAMING_INDEX = [ + self::OVERALL_OPENROAMING_LEVEL_NO => 'OVERALL_OPENROAMING_LEVEL_NO', + self::OVERALL_OPENROAMING_LEVEL_GOOD => 'OVERALL_OPENROAMING_LEVEL_GOOD', + self::OVERALL_OPENROAMING_LEVEL_NOTE => 'OVERALL_OPENROAMING_LEVEL_NOTE', + self::OVERALL_OPENROAMING_LEVEL_WARN => 'OVERALL_OPENROAMING_LEVEL_WARN', + self::OVERALL_OPENROAMING_LEVEL_ERROR => 'OVERALL_OPENROAMING_LEVEL_ERROR', + ]; + + const OPENROAMING_INDEX = [ + self::OVERALL_OPENROAMING_LEVEL_NO => 'OVERALL_OPENROAMING_LEVEL_NO', + ]; + + const CERT_STATUS_INDEX = [ + self::CERT_STATUS_OK => 'CERT_STATUS_OK', + self::CERT_STATUS_WARN => 'CERT_STATUS_WARN', + self::CERT_STATUS_ERROR => 'CERT_STATUS_ERROR', + ]; /** * generates a detailed log of which installer was downloaded @@ -867,15 +893,7 @@ public function getCollapsedAttributes($eap = []) } return $collapsedList; } - - const READINESS_LEVEL_NOTREADY = 0; - const READINESS_LEVEL_SUFFICIENTCONFIG = 1; - const READINESS_LEVEL_SHOWTIME = 2; - const CERT_STATUS_OK = 0; - const CERT_STATUS_WARN = 1; - const CERT_STATUS_ERROR = 2; - /** * Does the profile contain enough information to generate installers with * it? Silverbullet will always return TRUE; RADIUS profiles need to do some diff --git a/core/Federation.php b/core/Federation.php index 80f597be3..295de0001 100644 --- a/core/Federation.php +++ b/core/Federation.php @@ -525,6 +525,9 @@ public function listIdentityProviders($activeOnly = 0) // SELECT -> resource, not boolean while ($idpQuery = mysqli_fetch_object(/** @scrutinizer ignore-type */ $allIDPs)) { $idp = new IdP($idpQuery->inst_id); + if (!isset($idpQuery->realms)) { + $idpQuery->realms = ''; + } $name = $idp->name; $idpInfo = ['entityID' => $idp->identifier, 'title' => $name, diff --git a/core/IdP.php b/core/IdP.php index 581dbb640..68db951be 100644 --- a/core/IdP.php +++ b/core/IdP.php @@ -171,6 +171,12 @@ public function listDeployments(bool $activeOnly = FALSE) const PROFILES_INCOMPLETE = 0; const PROFILES_CONFIGURED = 1; const PROFILES_SHOWTIME = 2; + + const PROFILES_INDEX = [ + self::PROFILES_INCOMPLETE => 'PROFILES_INCOMPLETE', + self::PROFILES_CONFIGURED => 'PROFILES_CONFIGURED', + self::PROFILES_SHOWTIME => 'PROFILES_SHOWTIME', + ]; /** * looks through all the profiles of the inst and determines the highest prod-ready level among the profiles diff --git a/core/User.php b/core/User.php index 31b0f1a4d..7bdc93f8c 100644 --- a/core/User.php +++ b/core/User.php @@ -119,7 +119,7 @@ public function isFederationAdmin($federation = 0) /** * This function tests if the current user has been configured as the system superadmin, i.e. if the user is allowed - * to execute the 112365365321.php script + * to execute the 112365365321.php script and obtain read-only access to admin areas. * * @return boolean TRUE if the user is a superadmin, FALSE if not */ @@ -127,6 +127,18 @@ public function isSuperadmin() { return in_array($this->userName, \config\Master::SUPERADMINS); } + + + /** + * This function tests if the current user has been configured as the system superadmin, i.e. if the user is allowed + * obtain read-only access to admin areas. + * + * @return boolean TRUE if the user is a support member, FALSE if not + */ + public function isSupport() + { + return in_array($this->userName, \config\Master::SUPPORT); + } /** * This function tests if the current user is an ovner of a given IdP diff --git a/web/admin/edit_federation.php b/web/admin/edit_federation.php index 7a315eaec..427a47752 100644 --- a/web/admin/edit_federation.php +++ b/web/admin/edit_federation.php @@ -39,7 +39,7 @@ $fedPost = $_POST['fed_id']; -$my_fed = $validator->existingFederation($fedPost, $_SESSION['user']); +[$my_fed, $editMode] = $validator->existingFederationInt($fedPost, $_SESSION['user']); $fed_options = $my_fed->getAttributes(); /// product name (eduroam CAT), then term used for "federation", then actual name of federation. echo $deco->defaultPagePrelude(sprintf(_("%s: Editing %s '%s'"), \config\Master::APPEARANCE['productname'], $uiElements->nomenclatureFed, $my_fed->name)); @@ -49,6 +49,15 @@ +' + . 'button.newoption {visibility: hidden}' + . 'input {pointer-events: none} ' + . '.ui-sortable-handle {pointer-events: none}' + . ''); +} +?> @@ -90,5 +99,12 @@ "; + echo "
"; + if ($editMode === 'fullaccess') { + echo ""; + $discardLabel = _("Discard changes"); + } else { + $discardLabel = _("Return"); + } + echo "
"; echo $deco->footer(); diff --git a/web/admin/js/nro.js b/web/admin/js/nro.js index 08b9c58d6..949daa5d0 100644 --- a/web/admin/js/nro.js +++ b/web/admin/js/nro.js @@ -20,6 +20,43 @@ /* various jquery scripts for the NRO admin page */ +function row_filter(table) { + var linked = table.find('[id^="unlinked_ck_"]').is(':checked'); + var broken_cert = table.find('[id^="brokencert_ck_"]').is(':checked'); + var or_warn = table.find('[id^="or_ck_"]').is(':checked'); + var profile_warn = table.find('[id^="profile_ck_"]').is(':checked'); + var input = table.find('[id^="qsearch_"]').val().toLowerCase(); + var tr_visible; + var inp_found; + table.children("tr.idp_tr").each(function() { + tr_visible = true; + if (linked && $(this).hasClass('linked')) { + tr_visible = false; + } + if (tr_visible && broken_cert && $(this).hasClass('certok')) { + tr_visible = false; + } + if (tr_visible && or_warn && $(this).hasClass('orok')) { + tr_visible = false; + } + if (tr_visible && profile_warn && $(this).hasClass('profileok')) { + tr_visible = false; + } + if (tr_visible && input !== '') { + inp_found = $(this).find("span.inst_name:contains('"+input+"')").length; + if (inp_found == 0) { + tr_visible = false; + } + } + + if (tr_visible) { + $(this).show(); + } else { + $(this).hide(); + } + }); +} + $(document).ready(function() { // realm diagnostics $("#realmcheck").on('click', function() { @@ -52,45 +89,28 @@ $(document).ready(function() { // handler for the text filter (must take into account possible filtering // on linked status $('[id^="qsearch_"]').keyup(function() { - var input = $(this).val().toLowerCase(); - var this_row = $(this).parent().parent(); - var this_table = this_row.parent(); - var this_ck = this_row.find('input[id^="unlinked_ck_"]'); - var tr; - if (input === '') { - if (this_ck.is(':checked')) { - console.log("checked"); - this_table.children("tr.notlinked").show(); - } else { - console.log("unchecked"); - this_table.children("tr.idp_tr").show(); - } - } else { - if (this_ck.is(':checked')) { - this_table.children("tr.idp_tr").hide(); - this_table.find("span.inst_name:contains('"+input+"')").each(function() { - tr = $(this).parent().parent(); - if (tr.hasClass("notlinked")) { - tr.show(); - } - }); - } else { - this_table.children("tr.idp_tr").hide(); - this_table.find("span.inst_name:contains('"+input+"')").parent().parent().show(); - } - - } + var this_table = $(this).parent().parent().parent(); + row_filter(this_table); }); // the linked filter checkbox handler - $('[id^="unlinked_ck_"]').on('click', function() { + $(":checkbox").on('click', function() { var this_table = $(this).parent().parent().parent(); - if ($(this).is(':checked')) { - this_table.children("tr.linked").hide(); - } else { - this_table.children("tr.linked").show(); + row_filter(this_table); + }); + + $("#fed_selection").on('change', function() { + fed = $("#fed_selection option:selected").val(); + if (fed === "XX") { + return; } + $("#thirdrow").hide(); + document.location.href = "overview_federation.php?fed_id="+fed; }); + + + $("img.cat-icon").tooltip(); + }); diff --git a/web/admin/overview_federation.php b/web/admin/overview_federation.php index a479e0f5c..c9d5e8393 100644 --- a/web/admin/overview_federation.php +++ b/web/admin/overview_federation.php @@ -24,30 +24,15 @@ $deco = new \web\lib\admin\PageDecoration(); $uiElements = new web\lib\admin\UIElements(); $validator = new \web\lib\common\InputValidation(); +$cat = new \core\CAT(); // initialises Entity static members -$iconsPath = '../resources/images/icons/'; -//$OpenRoamingSymbol = "OpenRoaming"; +//$OpenRoamingSymbol = "OpenRoaming"; $OpenRoamingSymbol = "OR"; $stausIcons = [ \core\IdP::PROFILES_SHOWTIME => ['img' => 'Tabler/checks-green.svg', 'text' => _("At least one profile is fully configured and visible in the user interface")], \core\IdP::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")], ]; -$certStatusIcons = [ - \core\AbstractProfile::CERT_STATUS_OK => ['img' => 'Tabler/certificate-green.svg', 'text' => _("All certificates are valid long enough")], - \core\AbstractProfile::CERT_STATUS_WARN => ['img' => 'Tabler/certificate-red.svg', 'text' => _("At least one certificate is close to expiry")], - \core\AbstractProfile::CERT_STATUS_ERROR => ['img' => 'Tabler/certificate-off.svg', 'text' => _("At least one certificate either has expired or is very close to expiry")], -]; - - -$operoamingStatusIcons = [ - \core\AbstractProfile::OVERALL_OPENROAMING_LEVEL_GOOD => ['img' => 'Tabler/square-rounded-check-green.svg', 'text' => _("OpenRoaming appears to be configured properly")], - \core\AbstractProfile::OVERALL_OPENROAMING_LEVEL_NOTE => ['img' => 'Tabler/info-square-rounded-blue.svg', 'text' => _("There are some minor OpenRoaming configuration issues")], - \core\AbstractProfile::OVERALL_OPENROAMING_LEVEL_WARN => ['img' => 'Tabler/info-square-rounded-blue.svg', 'text' => _("There are some avarage level OpenRoaming configuration issues")], - \core\AbstractProfile::OVERALL_OPENROAMING_LEVEL_ERROR => ['img' => 'Tabler/alert-square-rounded-red.svg', 'text' => _("There are some critical OpenRoaming configuration issues")], - -]; - echo $deco->defaultPagePrelude(sprintf(_("%s: %s Management"), \config\Master::APPEARANCE['productname'], $uiElements->nomenclatureFed)); $user = new \core\User($_SESSION['user']); require_once "inc/click_button_js.php"; @@ -59,6 +44,8 @@ var hide_downloads = ""; + + nomenclatureFed); ?> -

@@ -91,7 +77,36 @@
- + isSuperadmin() || $user->isSupport()) { + $fed_id = ''; + $countryList = $cat->printCountryList(1); + $fedIdentifiers = array_keys($countryList); + if (isset($_GET['fed_id'])) { + [$fed, $editMode] = $validator->existingFederationInt($_GET['fed_id'], $_SESSION['user']); + $fed_id = $fed->tld; + $feds = [['name'=>'user:fedadmin', 'value' => $fed_id, 'mode' => '']]; + } else { + $feds = $user->getAttributes("user:fedadmin"); + $editMode = 'fullaccess'; + } + ?> +

Select a different federation

+ + + +
+
@@ -99,20 +114,14 @@ isSuperadmin() && isset($_GET['fed_id'])) { - $cat = new \core\CAT(); // initialises Entity static members - $fedIdentifiers = array_keys($cat->knownFederations); - $fed_id = htmlentities($_GET['fed_id'], ENT_QUOTES); - if (!in_array(strtoupper($fed_id), $fedIdentifiers)) { - throw new Exception($this->inputValidationError(sprintf("This %s does not exist!", \core\common\Entity::$nomenclature_fed))); + if (!$user->isSuperadmin() && !$user->isSupport()) { + if (!$user->isFederationAdmin()) { + echo "

".sprintf(_("You are not a %s manager."), $uiElements->nomenclatureFed)."

"; + echo $deco->footer(); + exit(0); + } else { + $feds = $user->getAttributes("user:fedadmin"); } - $feds = [['name'=>' user:fedadmin', 'value' => $fed_id]]; - } elseif (!$user->isFederationAdmin()) { - echo "

".sprintf(_("You are not a %s manager."), $uiElements->nomenclatureFed)."

"; - echo $deco->footer(); - exit(0); - } else { - $feds = $user->getAttributes("user:fedadmin"); } foreach ($feds as $onefed) { $thefed = new \core\Federation(strtoupper($onefed['value'])); @@ -144,9 +153,14 @@ infoblock($thefed->getAttributes(), "fed", "FED"); if ($readonly === FALSE) { + if ($editMode == 'fullaccess') { + $editLabel = _("Edit ..."); + } else { + $editLabel = _("View ..."); + } ?> -
+
listPendingInvitations(); if (\config\Master::DB['enforce-external-sync']) { - echo "".sprintf(_("%s Database Sync Status"), \config\ConfAssistant::CONSORTIUM['display_name']).""; + echo "".sprintf(_("%s Database Link Status"), \config\ConfAssistant::CONSORTIUM['display_name']).""; } ?> @@ -299,8 +313,11 @@ $thefed = new \core\Federation($fedId); /// nomenclature for 'federation', federation name, nomenclature for 'inst' echo "".sprintf(_("The following %s are in your %s %s:"), $uiElements->nomenclatureParticipant, $uiElements->nomenclatureFed, ''.$thefed->name.'').""; - echo "". _("Quick search:")." "; - echo " ". _("Only not linked"). ""; + echo "". _("Quick search:")." "; + echo ""; + echo ""; + echo ""; + echo ""; echo ""; // extract only pending invitations for *this* fed $display_pendings = FALSE; @@ -320,22 +337,79 @@ foreach ($my_idps as $index => $my_idp) { $idp_instance = $idps[$index]['instance']; - $idpLinked = 'nosync'; + // get max profile status + $profileClass = ''; + if ($idp_instance->maxProfileStatus() >= \core\IdP::PROFILES_SHOWTIME) { + $status = \core\IdP::PROFILES_SHOWTIME; + $profileClass = 'profileok'; + } elseif ($idp_instance->maxProfileStatus() >= \core\IdP::PROFILES_CONFIGURED) { + $status = \core\IdP::PROFILES_CONFIGURED; + $profileClass = 'profilewarn'; + } else { + $status = \core\IdP::PROFILES_INCOMPLETE; + $profileClass = 'profilewarn'; + } + $profileIconData = $uiElements->iconData(\core\IdP::PROFILES_INDEX[$status]); + $profileIcon = $uiElements->catIcon($profileIconData); + + // verify the certificates status for this IdP + if (isset($certStatus[$index])) { + $certIconData = $uiElements->iconData(\core\AbstractProfile::CERT_STATUS_INDEX[$certStatus[$index]]); + if ($certStatus[$index] > 0) { + $certClass = 'certproblem'; + } else { + $certClass = 'certok'; + } + } else { + $certIconData = $uiElements->iconData('CERTS_NOT_SHOWN'); + $certClass = 'certok'; + } + $certIcon = $uiElements->catIcon($certIconData); + + // verify DB sync status for this IdP + $linkClass = 'nosync'; + $linkIcon = ''; // external DB sync, if configured as being necessary if (\config\Master::DB['enforce-external-sync']) { switch ($idp_instance->getExternalDBSyncState()) { case \core\IdP::EXTERNAL_DB_SYNCSTATE_NOTSUBJECTTOSYNCING: break; case \core\IdP::EXTERNAL_DB_SYNCSTATE_SYNCED: - $idpLinked = 'linked'; + $linkClass = 'linked'; + $linkIcon = $uiElements->catIcon($uiElements->iconData('IDP_LINKED')); break; case \core\IdP::EXTERNAL_DB_SYNCSTATE_NOT_SYNCED: - $idpLinked = 'notlinked'; + $linkClass = 'notlinked'; + $linkIcon = $uiElements->catIcon($uiElements->iconData('IDP_NOT_LINKED')); break; } - } + } + + // verify the OpenRoaming status for this IdP + $orStatus = $idp_instance->maxOpenRoamingStatus(); + $orClass = 'orok'; + $orIcon = ''; + switch ($orStatus) { + case \core\AbstractProfile::OVERALL_OPENROAMING_LEVEL_NO: + $orIcon = "-"; + break; + case \core\AbstractProfile::OVERALL_OPENROAMING_LEVEL_GOOD: + break; + case \core\AbstractProfile::OVERALL_OPENROAMING_LEVEL_NOTE: + case \core\AbstractProfile::OVERALL_OPENROAMING_LEVEL_WARN: + case \core\AbstractProfile::OVERALL_OPENROAMING_LEVEL_ERROR: + $orClass = 'orwarn'; + break; + default: + throw new \Exception("Impossible OpenRoaming status!"); + } + if ($orIcon === "") { + $iconData = $uiElements->iconData(\core\AbstractProfile::OVERALL_OPENROAMING_INDEX[$status]); + $orIcon = $uiElements->catIcon($iconData); + } + // new row_id, with one IdP inside - echo ""; + echo ""; // name; and realm of silverbullet profiles if any // instantiating all profiles is costly, so we only do this if @@ -348,7 +422,7 @@ } } } - echo " + echo " " . "" @@ -363,62 +437,23 @@ . ""; // deployment status; need to dive into profiles for this // show happy eyeballs if at least one profile is configured/showtime - echo ""; + echo "$profileIcon"; + echo "$orIcon"; + echo "$certIcon"; - if ($idp_instance->maxProfileStatus() >= \core\IdP::PROFILES_SHOWTIME) { - $status = \core\IdP::PROFILES_SHOWTIME; - echo "".$stausIcons[$status]["; - } elseif ($idp_instance->maxProfileStatus() >= \core\IdP::PROFILES_CONFIGURED) { - $status = \core\IdP::PROFILES_CONFIGURED; - echo "".$stausIcons[$status]["; - } - -/* - echo ($idp_instance->maxProfileStatus() >= \core\IdP::PROFILES_CONFIGURED ? "C" : "-" ) - . " " - . ($idp_instance->maxProfileStatus() >= \core\IdP::PROFILES_SHOWTIME ? "V" : "-" ) - . " "; - * - */ - echo ""; - $status = $idp_instance->maxOpenRoamingStatus(); - switch ($status) { - case \core\AbstractProfile::OVERALL_OPENROAMING_LEVEL_NO: - echo "-"; - break; - case \core\AbstractProfile::OVERALL_OPENROAMING_LEVEL_GOOD: - case \core\AbstractProfile::OVERALL_OPENROAMING_LEVEL_NOTE: - case \core\AbstractProfile::OVERALL_OPENROAMING_LEVEL_WARN: - case \core\AbstractProfile::OVERALL_OPENROAMING_LEVEL_ERROR: - echo "".$operoamingStatusIcons[$status]["; - break; - default: - throw new \Exception("Impossible OpenRoaming status!"); - } - echo ""; - echo "".$certStatusIcons[$certStatus[$index]]["; // external DB sync, if configured as being necessary if (\config\Master::DB['enforce-external-sync']) { - echo ""; + echo ""; if ($readonly === FALSE) { - echo "
-   "; - } - switch ($idpLinked) { - case 'nosync': - break; - case 'linked': -// echo "
"._("Linked")."
"; - break; - case 'notlinked': - echo ""._("NOT linked").""; - break; + echo ""; + echo ""; + echo "
"; } - echo ""; + echo ""; } // admin management - echo ""; + echo ""; if ($readonly === FALSE) { echo "
diff --git a/web/admin/overview_org.php b/web/admin/overview_org.php index 39173b1b9..8b56ec703 100644 --- a/web/admin/overview_org.php +++ b/web/admin/overview_org.php @@ -121,7 +121,7 @@ function displayRadiusPropertyWidget(&$theProfile, $readonly, &$uiElements, $edi foreach ($attribs as $attrib) { if ($attrib['level'] == \core\Options::LEVEL_METHOD && !preg_match("/^internal:/", $attrib['name']) && !$justOnce) { $justOnce = TRUE; - $buffer_eaptypediv .= "" . _("Options on EAP Method/Device level are in effect.") . ""; + $buffer_eaptypediv .= "" . _("Options on EAP Method/Device level are in effect.") . ""; } } $buffer_eaptypediv .= "
"; @@ -147,16 +147,18 @@ function displayRadiusPropertyWidget(&$theProfile, $readonly, &$uiElements, $edi $certStatus = $theProfile->certificateStatus(); switch ($certStatus) { case core\AbstractProfile::CERT_STATUS_OK: - $buffer_headline .= "
" . $uiElements->boxCertOK("", sprintf(_("This profile is NOT shown on the user download interface, even though we have enough information to show. To enable the profile, add the attribute \"%s\" and tick the corresponding box."), $uiElements->displayName("profile:production")), TRUE); + $iconData = $uiElements->iconData('CERT_STATUS_OK'); + $buffer_headline .= "
" . $uiElements->catIcon(($iconData)); break; case core\AbstractProfile::CERT_STATUS_WARN: - $buffer_headline .= "
" . $uiElements->boxCertWarning("", sprintf(_("This profile is NOT shown on the user download interface, even though we have enough information to show. To enable the profile, add the attribute \"%s\" and tick the corresponding box."), $uiElements->displayName("profile:production")), TRUE); + $iconData = $uiElements->iconData('CERT_STATUS_WARN'); + $buffer_headline .= "
" . $uiElements->catIcon(($iconData)); break; case core\AbstractProfile::CERT_STATUS_ERROR: - $buffer_headline .= "
" . $uiElements->boxCertError("", sprintf(_("This profile is NOT shown on the user download interface, even though we have enough information to show. To enable the profile, add the attribute \"%s\" and tick the corresponding box."), $uiElements->displayName("profile:production")), TRUE); + $iconData = $uiElements->iconData('CERT_STATUS_ERROR'); + $buffer_headline .= "
" . $uiElements->catIcon(($iconData)); break; - } - + } $buffer_headline .= "
"; echo $buffer_headline; @@ -324,7 +326,7 @@ function displayDeploymentPropertyWidget(&$deploymentObject) { radius_status_1]['icon'] . "' alt='" . $radiusMessages[$deploymentObject->radius_status_1]['text'] . - "' title='" . $radiusMessages[$deploymentObject->radius_status_1]['text'] . "'>"; + "' title='" . $radiusMessages[$deploymentObject->radius_status_1]['text'] . "' class='cat-icon'>"; ?> @@ -347,7 +349,7 @@ function displayDeploymentPropertyWidget(&$deploymentObject) { radius_status_2]['icon'] . "' alt='" . $radiusMessages[$deploymentObject->radius_status_2]['text'] . - "' title='" . $radiusMessages[$deploymentObject->radius_status_2]['text'] . "'>"; + "' title='" . $radiusMessages[$deploymentObject->radius_status_2]['text'] . "' class='cat-icon'>"; ?> @@ -541,7 +543,14 @@ function displayClassicHotspotPropertyWidget($deploymentObject) { ?> - + + + + bodyTagCode(); ?>> productheader("ADMIN-PARTICIPANT"); diff --git a/web/lib/admin/UIElements.php b/web/lib/admin/UIElements.php index 00e72dcd9..5a1e9a888 100644 --- a/web/lib/admin/UIElements.php +++ b/web/lib/admin/UIElements.php @@ -57,7 +57,7 @@ class UIElements extends \core\common\Entity { * * @var string */ - public $nomenclatureParticipant; + public $nomenclatureParticipant; /** * Initialises the class. @@ -445,21 +445,29 @@ public function previewInfoFileinHTML($fileReference) { public function boxFlexible(int $level, string $text = NULL, string $caption = NULL, bool $omittabletags = FALSE) { \core\common\Entity::intoThePotatoes(); $uiMessages = [ - \core\common\Entity::L_OK => ['icon' => '../resources/images/icons/Tabler/square-rounded-check-filled-green.svg', 'text' => _("OK")], - \core\common\Entity::L_REMARK => ['icon' => '../resources/images/icons/Tabler/info-square-rounded-filled-blue.svg', 'text' => _("Remark")], - \core\common\Entity::L_WARN => ['icon' => '../resources/images/icons/Tabler/alert-square-rounded-filled-yellow.svg', 'text' => _("Warning!")], - \core\common\Entity::L_ERROR => ['icon' => '../resources/images/icons/Tabler/square-rounded-x-filled-red.svg', 'text' => _("Error!")], - \core\common\Entity::L_CERT_OK => ['icon' => '../resources/images/icons/Tabler/certificate-green.svg', 'text' => _("OK")], - \core\common\Entity::L_CERT_WARN => ['icon' => '../resources/images/icons/Tabler/certificate-red.svg', 'text' => _("Warning!")], - \core\common\Entity::L_CERT_ERROR => ['icon' => '../resources/images/icons/Tabler/certificate-off.svg', 'text' => _("Warning!")], + \core\common\Entity::L_OK => ['img' => 'Tabler/square-rounded-check-filled-green.svg', 'text' => _("OK")], + \core\common\Entity::L_REMARK => ['img' => 'Tabler/info-square-rounded-filled-blue.svg', 'text' => _("Remark")], + \core\common\Entity::L_WARN => ['img' => 'Tabler/alert-square-rounded-filled-yellow.svg', 'text' => _("Warning!")], + \core\common\Entity::L_ERROR => ['img' => 'Tabler/square-rounded-x-filled-red.svg', 'text' => _("Error!")], + \core\common\Entity::L_CERT_OK => ['img' => 'Tabler/certificate-green.svg', 'text' => _("OK")], + \core\common\Entity::L_CERT_WARN => ['img' => 'Tabler/certificate-red.svg', 'text' => _("Warning!")], + \core\common\Entity::L_CERT_ERROR => ['img' => 'Tabler/certificate-off.svg', 'text' => _("Warning!")], ]; - + $retval = ""; if (!$omittabletags) { $retval .= ""; } - $finalCaption = ($caption !== NULL ? $caption : $uiMessages[$level]['text']); - $retval .= "" . $finalCaption . ""; +// $finalCaption = ($caption !== NULL ? $caption : $uiMessages[$level]['text']); +// $retval .= "" . $finalCaption . ""; + $iconData = $uiMessages[$level]; + if ($caption !== NULL) { + $iconData['text'] = $caption; + } + + + $retval .= $this->catIcon($iconData); + if (!$omittabletags) { $retval .= ""; } @@ -664,5 +672,39 @@ public function sanityTestResultHTML($test) { } return($out); } - + /** + * prepares data for icons + * + * @param string $index + * @return array + */ + public function iconData($index) { + \core\common\Entity::intoThePotatoes(); + $icons = [ + 'CERT_STATUS_OK' => ['img' => 'Tabler/certificate-green.svg', 'text' => _("All certificates are valid long enough")], + 'CERT_STATUS_WARN' => ['img' => 'Tabler/certificate-red.svg', 'text' => _("At least one certificate is close to expiry")], + 'CERT_STATUS_ERROR' => ['img' => 'Tabler/certificate-off.svg', 'text' => _("At least one certificate either has expired or is very close to expiry")], + 'OVERALL_OPENROAMING_LEVEL_GOOD' => ['img' => 'Tabler/square-rounded-check-green.svg', 'text' => _("OpenRoaming appears to be configured properly")], + 'OVERALL_OPENROAMING_LEVEL_NOTE' => ['img' => 'Tabler/info-square-rounded-blue.svg', 'text' => _("There are some minor OpenRoaming configuration issues")], + 'OVERALL_OPENROAMING_LEVEL_WARN' => ['img' => 'Tabler/info-square-rounded-blue.svg', 'text' => _("There are some avarage level OpenRoaming configuration issues")], + 'OVERALL_OPENROAMING_LEVEL_ERROR' => ['img' => 'Tabler/alert-square-rounded-red.svg', 'text' => _("There are some critical OpenRoaming configuration issues")], + 'PROFILES_SHOWTIME' => ['img' => 'Tabler/checks-green.svg', 'text' => _("At least one profile is fully configured and visible in the user interface")], + '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")], + 'PROFILES_INCOMPLETE' => ['img' => 'Tabler/access-point-off-red.svg', 'text' => _("No configured profiles")], + 'IDP_LINKED' => ['img' => 'Tabler/database-green.svg', 'text' => _("Linked")], + 'IDP_NOT_LINKED' => ['img' => 'Tabler/database-off-red.svg', 'text' => _("NOT linked")], + 'CERTS_NOT_SHOWN' => ['img' => 'Tabler/question-mark-blue.svg', 'text' => _("Not showing cert info if no profiles are visible")], + ]; + \core\common\Entity::outOfThePotatoes(); + return($icons[$index]); + } + +/** + * the HTML img element produced 0n the basis of a simple [src,title] array + * @param type array + * @return string the img element + */ + public function catIcon($data) { + return "".$data["; + } } diff --git a/web/lib/common/InputValidation.php b/web/lib/common/InputValidation.php index 397e8c083..558144f9c 100644 --- a/web/lib/common/InputValidation.php +++ b/web/lib/common/InputValidation.php @@ -55,7 +55,6 @@ private function inputValidationError($customtext) */ public function existingFederation($input, $owner = NULL) { - $cat = new \core\CAT(); // initialises Entity static members $fedIdentifiers = array_keys($cat->knownFederations); if (!in_array(strtoupper($input), $fedIdentifiers)) { @@ -78,6 +77,40 @@ public function existingFederation($input, $owner = NULL) throw new Exception($this->inputValidationError(sprintf("User is not %s administrator!", \core\common\Entity::$nomenclature_fed))); } + /** + * Is this a known Federation? Optionally, also check if the authenticated + * user is a federation admin of that federation + * @param mixed $input the ISO code of the federation + * @param string|NULL $owner the authenticated username, optional + * @return array(\core\Federation, string) + * @throws Exception + */ + public function existingFederationInt($input, $owner = NULL) + { + $cat = new \core\CAT(); // initialises Entity static members + $fedIdentifiers = array_keys($cat->knownFederations); + if (!in_array(strtoupper($input), $fedIdentifiers)) { + throw new Exception($this->inputValidationError(sprintf("This %s does not exist!", \core\common\Entity::$nomenclature_fed))); + } + // totally circular, but this hopefully *finally* make Scrutinizer happier + $correctIndex = array_search(strtoupper($input), $fedIdentifiers); + $postFed = $fedIdentifiers[$correctIndex]; + $temp = new \core\Federation($postFed); + if ($owner === NULL) { + return [$temp,'readonly']; + } + $user = new \core\User($owner); + foreach ($temp->listFederationAdmins() as $oneowner) { + if ($oneowner == $owner) { + return [$temp, 'fullaccess']; + } + } + if ($user->isSuperadmin()|| $user->isSupport()) { + $this->loggerInstance->debug(4, "You are the superadmin/support\n"); + return [$temp,'readonly']; + } + throw new Exception($this->inputValidationError(sprintf("User is not %s administrator!", \core\common\Entity::$nomenclature_fed))); + } /** * Is this a known IdP? Optionally, also check if the authenticated @@ -112,7 +145,7 @@ public function existingIdP($input, $owner = NULL, $claimedFedBinding = NULL) /** * Is this a known IdP? Optionally, also check if the authenticated * user is an admin of that IdP or a federation admin for the parent federation - * federaton admins get read-only access, superadmins get readonly access as well + * federaton admins superadmin and support get read-only access, superadmins get readonly access as well * @param mixed $input the numeric ID of the IdP in the system * @param string $owner the authenticated username, optional * @param \core\Federation $claimedFedBinding if set, cross-check that IdP belongs to specified federation (useful in admin API mode) @@ -137,8 +170,8 @@ public function existingIdPInt($input, $owner = NULL, $claimedFedBinding = NULL) $this->loggerInstance->debug(4, "You are fed admin for this IdP\n"); return [$temp,'readonly']; } - if ($user->isSuperadmin()) { - $this->loggerInstance->debug(4, "You are the superadmin\n"); + if ($user->isSuperadmin() || $user->isSupport()) { + $this->loggerInstance->debug(4, "You are the superadmin/support\n"); return [$temp,'readonly']; } throw new Exception($this->inputValidationError("This IdP identifier is not accessible!")); diff --git a/web/resources/css/cat.css.php b/web/resources/css/cat.css.php index 8205b84a1..fa781fd11 100644 --- a/web/resources/css/cat.css.php +++ b/web/resources/css/cat.css.php @@ -964,12 +964,17 @@ padding-: 4px; padding-: 4px; } + table.user_overview td { border-top-style: none; padding-: 4px; padding-: 4px; - padding-top: 0px; - height: 25px; + vertical-align: middle; + height: 28px; +} + +table.user_overview img { + vertical-align: middle; } table.user_overview td:first-child { diff --git a/web/resources/images/icons/LICENSE b/web/resources/images/icons/LICENSE index afbb23a5a..2f6d799af 100644 --- a/web/resources/images/icons/LICENSE +++ b/web/resources/images/icons/LICENSE @@ -4,3 +4,5 @@ http://www.iconarchive.com/show/multipurpose-alphabet-icons-by-hydrattz.html License: "Free for non-commercial use, Commercial Usage not allowed" button_cancel.png: stock RESTENA image + +Tabler icons downloaded from https://tabler.io/ and are under the MIT license diff --git a/web/resources/images/icons/Tabler/access-point-off-red.svg b/web/resources/images/icons/Tabler/access-point-off-red.svg new file mode 100644 index 000000000..c3714e766 --- /dev/null +++ b/web/resources/images/icons/Tabler/access-point-off-red.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/resources/images/icons/Tabler/certificate-green.svg b/web/resources/images/icons/Tabler/certificate-green.svg index 7cc1f9512..da871b2e9 100644 --- a/web/resources/images/icons/Tabler/certificate-green.svg +++ b/web/resources/images/icons/Tabler/certificate-green.svg @@ -1,4 +1,4 @@ - + diff --git a/web/resources/images/icons/Tabler/check-green.svg b/web/resources/images/icons/Tabler/check-green.svg index 278d0eb01..14032b999 100644 --- a/web/resources/images/icons/Tabler/check-green.svg +++ b/web/resources/images/icons/Tabler/check-green.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/web/resources/images/icons/Tabler/checks-green.svg b/web/resources/images/icons/Tabler/checks-green.svg index 777672b9e..a53acfc76 100644 --- a/web/resources/images/icons/Tabler/checks-green.svg +++ b/web/resources/images/icons/Tabler/checks-green.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/web/resources/images/icons/Tabler/database-green.svg b/web/resources/images/icons/Tabler/database-green.svg new file mode 100644 index 000000000..91ca02022 --- /dev/null +++ b/web/resources/images/icons/Tabler/database-green.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/resources/images/icons/Tabler/database-off-red.svg b/web/resources/images/icons/Tabler/database-off-red.svg new file mode 100644 index 000000000..7660b9876 --- /dev/null +++ b/web/resources/images/icons/Tabler/database-off-red.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/resources/images/icons/Tabler/question-mark-blue.svg b/web/resources/images/icons/Tabler/question-mark-blue.svg new file mode 100644 index 000000000..164cbc07d --- /dev/null +++ b/web/resources/images/icons/Tabler/question-mark-blue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/resources/images/icons/Tabler/square-rounded-check-green.svg b/web/resources/images/icons/Tabler/square-rounded-check-green.svg index 2e9b9d65b..421f10fc9 100644 --- a/web/resources/images/icons/Tabler/square-rounded-check-green.svg +++ b/web/resources/images/icons/Tabler/square-rounded-check-green.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file