From abd63033f76954322648eb4c0411defaf2fda1f1 Mon Sep 17 00:00:00 2001 From: zachmann <gabriel.zachmann@kit.edu> Date: Fri, 29 Jul 2022 10:24:44 +0200 Subject: [PATCH] improve capability handling --- CHANGELOG.md | 8 +++ .../web/partials/capabilities-part.mustache | 5 -- .../partials/capability-single-part.mustache | 30 +++++++----- .../server/web/partials/restrictions.mustache | 1 + .../partials/sub-capabilities-part.mustache | 5 -- .../sub-capability-single-part.mustache | 29 ++++++----- internal/server/web/static/css/mytoken.css | 8 +++ internal/server/web/static/css/restr-gui.css | 13 +++-- internal/server/web/static/js/capabilities.js | 49 ++++++++++++++++--- internal/server/web/static/js/consent.js | 14 +++++- internal/server/web/static/js/create-mt.js | 6 +-- 11 files changed, 117 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e41f5ab..c51fc0a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,14 @@ - Trusted web applications can skip the consent screen +### Enhancements + +- Reworked and improved several parts of the web interface: + - Consent Screen: On default a more compressed view is shown, where sections can be expanded if needed. + - Capabilities: + - Simplified the checking of capabilities + - Read/Write capabilities are now not split but can be toggled + ### Dependencies - Bump github.com/valyala/fasthttp from 1.37.0 to 1.38.0 diff --git a/internal/server/web/partials/capabilities-part.mustache b/internal/server/web/partials/capabilities-part.mustache index 48467087..a2184383 100644 --- a/internal/server/web/partials/capabilities-part.mustache +++ b/internal/server/web/partials/capabilities-part.mustache @@ -3,11 +3,6 @@ {{> capability-single-part}} {{/ReadWriteCapability}} <ul class="list-group"> - {{#ReadOnlyCapability}} - <li class="list-group-item capability"> - {{> capability-single-part}} - </li> - {{/ReadOnlyCapability}} {{#Children}} {{> capabilities-part}} {{/Children}} diff --git a/internal/server/web/partials/capability-single-part.mustache b/internal/server/web/partials/capability-single-part.mustache index 4d029b9d..9a8a858b 100644 --- a/internal/server/web/partials/capability-single-part.mustache +++ b/internal/server/web/partials/capability-single-part.mustache @@ -6,21 +6,29 @@ <div class="d-flex justify-content-between"> <h6> {{Name}} - <span class="pl-1" style="font-size: 1rem;"> - {{^IsReadOnly}} - <i class="fas fa-pencil-alt" data-toggle="tooltip" data-placement="top" title="" - data-original-title="Allows full access."></i> - {{/IsReadOnly}} - {{#IsReadOnly}} - <i class="fas fa-book-open" data-toggle="tooltip" data-placement="top" title="" - data-original-title="Allows only read access."></i> - {{/IsReadOnly}} + <span class="pl-1" style="font-size: 1rem;" data-toggle="tooltip" data-placement="top" title="" + data-original-title=""> + {{#ReadOnlyCapability}} + <input id="cp-{{Name}}-mode" class="rw-cap-mode" type="checkbox" checked data-size="sm" + data-toggle="toggle" + data-onstyle="dark" data-offstyle="dark" data-style="round" + data-on='<i class="fas fa-pencil-alt"></i>' + data-off='<i class="fas fa-book-open"></i>'> + {{/ReadOnlyCapability}} </span> </h6> - <i class="fas fa-exclamation-circle {{ColorClass}}" data-toggle="tooltip" + <i class="fas fa-exclamation-circle {{ColorClass}} rw-cap-write" data-toggle="tooltip" data-placement="right" title="" data-original-title="{{CapabilityLevel}}"></i> + {{#ReadOnlyCapability}} + <i class="fas fa-exclamation-circle {{ColorClass}} rw-cap-read d-none" data-toggle="tooltip" + data-placement="right" title="" data-original-title="{{CapabilityLevel}}"></i> + {{/ReadOnlyCapability}} </div> - <small>{{Description}}</small> + <small class="rw-cap-write">{{Description}}</small> + {{#ReadOnlyCapability}} + <small class="rw-cap-read d-none">{{Description}}</small> + {{/ReadOnlyCapability}} + {{#IsCreateMT}} <small><br>A created mytoken can have the following capabilities:</small> diff --git a/internal/server/web/partials/restrictions.mustache b/internal/server/web/partials/restrictions.mustache index ad0fefb1..8ba96cc0 100644 --- a/internal/server/web/partials/restrictions.mustache +++ b/internal/server/web/partials/restrictions.mustache @@ -3,6 +3,7 @@ <div> <span id="restr-editor-wrap" class="{{#collapse}} d-none{{/collapse}}"> <input id="restr-editor-mode" type="checkbox" checked data-size="sm" data-toggle="toggle" + data-style="restr-editor" data-onstyle="success" data-offstyle="info" data-on="Easy Editor" data-off="JSON Editor"></span> {{#collapse}} <button class="btn btn-info my-expand ml-2" type="button" data-toggle="collapse" data-target="#restr-body" diff --git a/internal/server/web/partials/sub-capabilities-part.mustache b/internal/server/web/partials/sub-capabilities-part.mustache index a91b71d4..d3c112e8 100644 --- a/internal/server/web/partials/sub-capabilities-part.mustache +++ b/internal/server/web/partials/sub-capabilities-part.mustache @@ -3,11 +3,6 @@ {{> sub-capability-single-part}} {{/ReadWriteCapability}} <ul class="list-group"> - {{#ReadOnlyCapability}} - <li class="list-group-item capability"> - {{> sub-capability-single-part}} - </li> - {{/ReadOnlyCapability}} {{#Children}} {{> sub-capabilities-part}} {{/Children}} diff --git a/internal/server/web/partials/sub-capability-single-part.mustache b/internal/server/web/partials/sub-capability-single-part.mustache index 3d445529..34bd9736 100644 --- a/internal/server/web/partials/sub-capability-single-part.mustache +++ b/internal/server/web/partials/sub-capability-single-part.mustache @@ -6,20 +6,27 @@ <div class="d-flex justify-content-between"> <h6> {{Name}} - <span class="pl-1" style="font-size: 1rem;"> - {{^IsReadOnly}} - <i class="fas fa-pencil-alt" data-toggle="tooltip" data-placement="top" title="" - data-original-title="Allows full access."></i> - {{/IsReadOnly}} - {{#IsReadOnly}} - <i class="fas fa-book-open" data-toggle="tooltip" data-placement="top" title="" - data-original-title="Allows only read access. Click to allow full access."></i> - {{/IsReadOnly}} + <span class="pl-1" style="font-size: 1rem;" data-toggle="tooltip" data-placement="top" title="" + data-original-title=""> + {{#ReadOnlyCapability}} + <input id="sub-cp-{{Name}}-mode" class="rw-cap-mode" type="checkbox" checked data-size="sm" + data-toggle="toggle" + data-onstyle="light" data-offstyle="light" data-style="round" + data-on='<i class="fas fa-pencil-alt"></i>' + data-off='<i class="fas fa-book-open"></i>'> + {{/ReadOnlyCapability}} </span> </h6> - <i class="fas fa-exclamation-circle {{ColorClass}}" data-toggle="tooltip" + <i class="fas fa-exclamation-circle {{ColorClass}} rw-cap-write" data-toggle="tooltip" data-placement="right" title="" data-original-title="{{CapabilityLevel}}"></i> + {{#ReadOnlyCapability}} + <i class="fas fa-exclamation-circle {{ColorClass}} rw-cap-read d-none" data-toggle="tooltip" + data-placement="right" title="" data-original-title="{{CapabilityLevel}}"></i> + {{/ReadOnlyCapability}} </div> - <small>{{Description}}</small> + <small class="rw-cap-write">{{Description}}</small> + {{#ReadOnlyCapability}} + <small class="rw-cap-read d-none">{{Description}}</small> + {{/ReadOnlyCapability}} </div> </div> diff --git a/internal/server/web/static/css/mytoken.css b/internal/server/web/static/css/mytoken.css index 02b8df80..4b251f39 100644 --- a/internal/server/web/static/css/mytoken.css +++ b/internal/server/web/static/css/mytoken.css @@ -42,4 +42,12 @@ ul.list-group li.list-group-item { border-top: 1px solid rgba(0, 0, 0, .15); border-left: 1px solid rgba(0, 0, 0, .15); border-right: 1px solid rgba(0, 0, 0, .15); +} + +.toggle, .toggle-on, .toggle-off { + border-radius: 0.3rem; +} + +.toggle.round, .toggle-on.round, .toggle-off.round { + border-radius: 20rem; } \ No newline at end of file diff --git a/internal/server/web/static/css/restr-gui.css b/internal/server/web/static/css/restr-gui.css index 20deac8d..a449bd81 100644 --- a/internal/server/web/static/css/restr-gui.css +++ b/internal/server/web/static/css/restr-gui.css @@ -1,12 +1,15 @@ -.table-item{ +.table-item { padding: 0.375rem 0.75rem; } -.toggle{ - width: 110px!important; + +.toggle.restr-editor, .toggle-on.restr-editor, .toggle-off.restr-editor { + width: 110px !important; } -.btn-add-list-item{ + +.btn-add-list-item { padding: 0; } -.btn-delete-list-item{ + +.btn-delete-list-item { padding: 0; } diff --git a/internal/server/web/static/js/capabilities.js b/internal/server/web/static/js/capabilities.js index 22a2352f..3f672aaa 100644 --- a/internal/server/web/static/js/capabilities.js +++ b/internal/server/web/static/js/capabilities.js @@ -9,6 +9,9 @@ const $capSummarySettings = $('#cap-summary-settings'); const $capSummaryHowManyGreen = $('#cap-summary-count-green'); const $capSummaryHowManyYellow = $('#cap-summary-count-yellow'); const $capSummaryHowManyRed = $('#cap-summary-count-red'); +const $capRWModes = $('.rw-cap-mode'); + +const rPrefix = "read@"; $capabilityChecks.click(function () { checkThisCapability.call(this); @@ -51,22 +54,28 @@ $(document).ready(function () { $('#subtokenCapabilities').hideB(); } updateCapSummary(); + $capRWModes.trigger('update-change'); }) function getCheckedCapabilities() { - return _getCheckedCapabilities($capabilityChecks); + return _getCheckedCapabilities($capabilityChecks, 'cp'); } function getCheckedSubtokenCapabilities() { if (!$capabilityCreateMytoken.prop("checked")) { return []; } - return _getCheckedCapabilities($subtokenCapabilityChecks); + return _getCheckedCapabilities($subtokenCapabilityChecks, 'sub-cp'); } -function _getCheckedCapabilities($checks) { +function _getCheckedCapabilities($checks, idPrefix) { let caps = $checks.filter(':checked').map(function (_, el) { - return $(el).val(); + let v = $(el).val(); + let $rw = $('#' + escapeSelector(idPrefix + '-' + rPrefix + v + '-mode')); + if ($rw.length && !$rw.prop('checked')) { + v = rPrefix + v; + } + return v; }).get(); caps = caps.filter(filterCaps); return caps; @@ -86,7 +95,6 @@ function filterCaps(c, i, caps) { } function isChildCapability(a, b) { - const rPrefix = "read@"; let aReadOnly = a.startsWith(rPrefix); let bReadOnly = b.startsWith(rPrefix); if (aReadOnly) { @@ -149,7 +157,7 @@ function updateCapSummary() { continue; } let name = $(c).val(); - let $icon = $($(c).closest('li.list-group-item').find('i.fa-exclamation-circle')[0]); + let $icon = $($(c).closest('li.list-group-item').find('i.fa-exclamation-circle').not('.d-none')[0]); if ($icon.hasClass('text-success')) { counter['green'][name] = 1; } @@ -199,4 +207,31 @@ function updateCapSummary() { } else { $capSummarySettings.attr('data-original-title', "This mytoken cannot be used to change settings."); } -} \ No newline at end of file +} + +$capRWModes.on('update-change', function () { + let write = $(this).prop('checked'); + if (write) { + $(this).closest('span').attr('data-original-title', `Allows full access. Click to only allow read access.`); + $(this).closest('div.flex-fill').find('.rw-cap-read').hideB(); + $(this).closest('div.flex-fill').find('.rw-cap-write').showB(); + } else { + $(this).closest('span').attr('data-original-title', `Allows only read access. Click to allow full access.`); + $(this).closest('div.flex-fill').find('.rw-cap-write').hideB(); + $(this).closest('div.flex-fill').find('.rw-cap-read').showB(); + } +}); + +$capRWModes.change(function () { + let write = $(this).prop('checked'); + $(this).trigger('update-change'); + let $modes = $(this).closest('li.list-group-item').find('.rw-cap-mode'); + $modes.bootstrapToggle(write ? 'on' : 'off', true); + $modes.trigger('update-change'); + if (!write) { + let $p = $(this).parents('li.list-group-item').children('div').find('.rw-cap-mode'); + $p.bootstrapToggle('off', true); + $p.trigger('update-change'); + } + updateCapSummary(); +}); \ No newline at end of file diff --git a/internal/server/web/static/js/consent.js b/internal/server/web/static/js/consent.js index 9dbcc9dc..dd2c10df 100644 --- a/internal/server/web/static/js/consent.js +++ b/internal/server/web/static/js/consent.js @@ -10,10 +10,20 @@ $(function (){ } updateRotationIcon(); checkedCapabilities.forEach(function (value) { - $('#cp-' + escapeSelector(value)).prop("checked", true) + let rCap = value.startsWith(rPrefix); + if (rCap) { + value = value.substring(rPrefix.length); + } + $('#cp-' + escapeSelector(value)).prop("checked", true); + $('#cp-' + escapeSelector(rPrefix + value) + '-mode').bootstrapToggle(rCap ? 'off' : 'on'); }) checkedSubtokenCapabilities.forEach(function (value) { - $('#sub-cp-' + escapeSelector(value)).prop("checked", true) + let rCap = value.startsWith(rPrefix); + if (rCap) { + value = value.substring(rPrefix.length); + } + $('#sub-cp-' + escapeSelector(value)).prop("checked", true); + $('#sub-cp-' + escapeSelector(rPrefix + value) + '-mode').bootstrapToggle(rCap ? 'off' : 'on'); }) chainFunctions( discovery, diff --git a/internal/server/web/static/js/create-mt.js b/internal/server/web/static/js/create-mt.js index dbee9ecd..049a2f2a 100644 --- a/internal/server/web/static/js/create-mt.js +++ b/internal/server/web/static/js/create-mt.js @@ -182,26 +182,22 @@ function polling(code, interval) { return; case "access_denied": message = "You denied the authorization request."; - window.clearInterval(intervalID); break; case "expired_token": message = "Code expired. You might want to restart the flow."; - window.clearInterval(intervalID); break; case "invalid_grant": case "invalid_token": message = "Code already used."; - window.clearInterval(intervalID); break; case "undefined": message = "No response from server"; - window.clearInterval(intervalID); break; default: message = getErrorMessage(errRes); - window.clearInterval(intervalID); break; } + window.clearInterval(intervalID); mtShowError(message) } }); -- GitLab