From b342ab14a79f5234ac72d194162a0495ca51206b Mon Sep 17 00:00:00 2001
From: zachmann <gabriel.zachmann@kit.edu>
Date: Thu, 28 Jul 2022 10:21:06 +0200
Subject: [PATCH] improve capability handling

---
 .../partials/capability-single-part.mustache  |   4 -
 .../sub-capability-single-part.mustache       |   4 -
 internal/server/web/static/js/capabilities.js | 106 ++++++++++++------
 3 files changed, 71 insertions(+), 43 deletions(-)

diff --git a/internal/server/web/partials/capability-single-part.mustache b/internal/server/web/partials/capability-single-part.mustache
index 1a248055..4d029b9d 100644
--- a/internal/server/web/partials/capability-single-part.mustache
+++ b/internal/server/web/partials/capability-single-part.mustache
@@ -1,10 +1,6 @@
 <div class="d-flex justify-content-between">
     <div class="mr-2">
         <input class="capability-check mr-1" id="cp-{{Name}}" type="checkbox" value="{{Name}}">
-        <span>
-        <i class="fas fa-check-circle text-success capability-active d-none"></i>
-        <i class="fas fa-times-circle text-danger capability-inactive"></i>
-    </span>
     </div>
     <div class="flex-fill">
         <div class="d-flex justify-content-between">
diff --git a/internal/server/web/partials/sub-capability-single-part.mustache b/internal/server/web/partials/sub-capability-single-part.mustache
index e2a8a73f..3d445529 100644
--- a/internal/server/web/partials/sub-capability-single-part.mustache
+++ b/internal/server/web/partials/sub-capability-single-part.mustache
@@ -1,10 +1,6 @@
 <div class="d-flex justify-content-between">
     <div class="mr-2">
         <input class="subtoken-capability-check mr-1" id="sub-cp-{{Name}}" type="checkbox" value="{{Name}}">
-        <span>
-        <i class="fas fa-check-circle text-success subtoken-capability-active d-none"></i>
-        <i class="fas fa-times-circle text-danger subtoken-capability-inactive"></i>
-    </span>
     </div>
     <div class="flex-fill">
         <div class="d-flex justify-content-between">
diff --git a/internal/server/web/static/js/capabilities.js b/internal/server/web/static/js/capabilities.js
index d0e9ed2f..22a2352f 100644
--- a/internal/server/web/static/js/capabilities.js
+++ b/internal/server/web/static/js/capabilities.js
@@ -37,26 +37,10 @@ function checkThisCapability() {
 
 function _checkThisCapability(prefix) {
     let activated = $(this).prop('checked');
-    let classActive = '.' + prefix + '-active';
-    let classInactive = '.' + prefix + '-inactive';
     let classCheck = '.' + prefix + '-check'
-    if (activated) {
-        let allSubCPInactive = $(this).closest('li.list-group-item').find(classInactive);
-        let allSubCPActive = $(this).closest('li.list-group-item').find(classActive);
-        allSubCPInactive.hideB();
-        allSubCPActive.showB();
-    } else {
-        let $checkedParents = $(this).parents('li.list-group-item').children('div').children('div').children(classCheck + ':checked');
-        if ($checkedParents.length === 0) {
-            let $span = $(this).siblings('span');
-            $span.find(classActive).hideB();
-            $span.find(classInactive).showB();
-        }
-        let $allSubCPChecks = $(this).closest('li.list-group-item').find('ul.list-group li.list-group-item' +
-            ' ' + classCheck);
-        $allSubCPChecks.each(function () {
-            _checkThisCapability.call(this, prefix);
-        })
+    $(this).closest('li.list-group-item').find(classCheck).prop('checked', activated);
+    if (!activated) {
+        $(this).parents('li.list-group-item').children('div').children('div').children(classCheck).prop('checked', false);
     }
 }
 
@@ -81,9 +65,50 @@ function getCheckedSubtokenCapabilities() {
 }
 
 function _getCheckedCapabilities($checks) {
-    return $checks.filter(':checked').map(function (_, el) {
+    let caps = $checks.filter(':checked').map(function (_, el) {
         return $(el).val();
     }).get();
+    caps = caps.filter(filterCaps);
+    return caps;
+}
+
+function filterCaps(c, i, caps) {
+    for (let j = 0; j < caps.length; j++) {
+        if (i === j) {
+            continue;
+        }
+        let cc = caps[j];
+        if (isChildCapability(c, cc)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+function isChildCapability(a, b) {
+    const rPrefix = "read@";
+    let aReadOnly = a.startsWith(rPrefix);
+    let bReadOnly = b.startsWith(rPrefix);
+    if (aReadOnly) {
+        a = a.substring(rPrefix.length);
+    }
+    if (bReadOnly) {
+        b = b.substring(rPrefix.length);
+    }
+    let aParts = a.split(':');
+    let bParts = b.split(':');
+    if (bReadOnly && !aReadOnly) {
+        return false;
+    }
+    if (bParts.length > aParts.length) {
+        return false;
+    }
+    for (let i = 0; i < bParts.length; i++) {
+        if (aParts[i] !== bParts[i]) {
+            return false;
+        }
+    }
+    return true;
 }
 
 function getCheckedCapabilitesAndSubtokencapabilities() {
@@ -104,36 +129,47 @@ function searchAllChecked(str) {
 }
 
 function updateCapSummary() {
-    let howManyGreenCaps = 0;
-    let howManyYellowCaps = 0;
-    let howManyRedCaps = 0;
     let at = $capabilityAT.prop("checked") || $('#sub-cp-AT').prop("checked");
     let mt = $capabilityCreateMytoken.prop("checked");
     let info = searchAllChecked("tokeninfo");
     let settings = searchAllChecked("settings");
 
-    for (const c of $.merge($capabilityChecks, $subtokenCapabilityChecks)) {
+    let all = [];
+    $.merge(all, $capabilityChecks);
+    if ($capabilityCreateMytoken.prop('checked')) {
+        $.merge(all, $subtokenCapabilityChecks);
+    }
+    let counter = {
+        'green': {},
+        'yellow': {},
+        'red': {}
+    }
+    for (const c of all) {
         if (!c.checked) {
             continue;
         }
-        let $icon = $(c).closest('li.list-group-item').find('i.fa-exclamation-circle');
+        let name = $(c).val();
+        let $icon = $($(c).closest('li.list-group-item').find('i.fa-exclamation-circle')[0]);
         if ($icon.hasClass('text-success')) {
-            howManyGreenCaps++;
+            counter['green'][name] = 1;
         }
         if ($icon.hasClass('text-warning')) {
-            howManyYellowCaps++;
+            counter['yellow'][name] = 1;
         }
         if ($icon.hasClass('text-danger')) {
-            howManyRedCaps++;
+            counter['red'][name] = 1;
         }
     }
-
-    $capSummaryHowManyGreen.text(howManyGreenCaps);
-    $capSummaryHowManyYellow.text(howManyYellowCaps);
-    $capSummaryHowManyRed.text(howManyRedCaps);
-    $capSummaryHowManyGreen.attr('data-original-title', `This mytoken has ${howManyGreenCaps} normal capabilities.`);
-    $capSummaryHowManyYellow.attr('data-original-title', `This mytoken has ${howManyYellowCaps} powerful capabilities.`);
-    $capSummaryHowManyRed.attr('data-original-title', `This mytoken has ${howManyRedCaps} very powerful capabilities.`);
+    let greens = Object.keys(counter['green']).length;
+    let yellows = Object.keys(counter['yellow']).length;
+    let reds = Object.keys(counter['red']).length;
+
+    $capSummaryHowManyGreen.text(greens);
+    $capSummaryHowManyYellow.text(yellows);
+    $capSummaryHowManyRed.text(reds);
+    $capSummaryHowManyGreen.attr('data-original-title', `This mytoken has ${greens} normal capabilities.`);
+    $capSummaryHowManyYellow.attr('data-original-title', `This mytoken has ${yellows} powerful capabilities.`);
+    $capSummaryHowManyRed.attr('data-original-title', `This mytoken has ${reds} very powerful capabilities.`);
 
     $capSummaryAT.removeClass("text-success");
     $capSummaryMT.removeClass("text-success");
-- 
GitLab