From 16466fddd9f41fa389524ab04ecbf116e05b8e17 Mon Sep 17 00:00:00 2001
From: zachmann <gabriel.zachmann@kit.edu>
Date: Mon, 1 Aug 2022 11:48:42 +0200
Subject: [PATCH] rework settings web interface

---
 CHANGELOG.md                                  |   5 +
 internal/server/handlers.go                   |  54 ++++++---
 internal/server/server.go                     |   1 -
 internal/server/web/partials/grants.mustache  | 114 +++++++++---------
 internal/server/web/partials/scripts.mustache |   5 +-
 .../{ssh.mustache => settings-ssh.mustache}   |  53 +-------
 internal/server/web/static/css/mytoken.css    |  13 ++
 internal/server/web/static/js/settings.js     |  11 +-
 internal/server/web/static/js/ssh.js          |  36 ++----
 9 files changed, 137 insertions(+), 155 deletions(-)
 rename internal/server/web/sites/{ssh.mustache => settings-ssh.mustache} (77%)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c51fc0a9..afefa560 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,11 @@
   - Capabilities:
     - Simplified the checking of capabilities
     - Read/Write capabilities are now not split but can be toggled
+  - Settings:
+    - Grant Types:
+      - Include pages of different grant types in this view.
+      - Grant Types can be expanded (collapsed on default).
+      - Link to grant type page that was not clear enough is no longer needed.
  
 ### Dependencies
 
diff --git a/internal/server/handlers.go b/internal/server/handlers.go
index 7881e4e7..db2ac421 100644
--- a/internal/server/handlers.go
+++ b/internal/server/handlers.go
@@ -1,7 +1,10 @@
 package server
 
 import (
+	"strings"
+
 	"github.com/gofiber/fiber/v2"
+	"github.com/pkg/errors"
 
 	"github.com/oidc-mytoken/server/internal/config"
 	consent "github.com/oidc-mytoken/server/internal/endpoints/consent/pkg"
@@ -36,35 +39,48 @@ func handleHome(ctx *fiber.Ctx) error {
 }
 
 func handleSettings(ctx *fiber.Ctx) error {
-	binding := map[string]interface{}{
-		loggedIn:   true,
-		"settings": true,
-		"grants": []struct {
-			DisplayName string
-			Name        string
-			Description string
-			Link        string
-		}{
-			{
-				DisplayName: "SSH",
-				Name:        "ssh",
-				Description: "The SSH grant type allows you to link an ssh key and use ssh authentication for various actions.",
-				Link:        "/settings/grants/ssh",
+	type bindData struct {
+		DisplayName string
+		Name        string
+		Description string
+		Link        string
+		EmbedBody   string
+		partialName string
+		bindingData map[string]interface{}
+	}
+	grants := []*bindData{
+		{
+			DisplayName: "SSH",
+			Name:        "ssh",
+			Description: "The SSH grant type allows you to link an ssh key and use ssh authentication for various actions.",
+			Link:        "/settings/grants/ssh",
+			partialName: "sites/settings-ssh",
+			bindingData: map[string]interface{}{
+				"restr-gui":             true,
+				"restrictions":          consent.WebRestrictions{},
+				"capabilities":          consent.AllWebCapabilities(),
+				"subtoken-capabilities": consent.AllWebCapabilities(),
 			},
 		},
 	}
-	return ctx.Render("sites/settings", binding, layoutMain)
-}
-func handleSSH(ctx *fiber.Ctx) error {
+	for _, g := range grants {
+		embed := strings.Builder{}
+		if err := errors.WithStack(serverConfig.Views.Render(&embed, g.partialName, g.bindingData)); err != nil {
+			return err
+		}
+		g.EmbedBody = embed.String()
+	}
 	binding := map[string]interface{}{
-		"settings-ssh":          true,
+		"grants":                grants,
 		loggedIn:                true,
+		"settings":              true,
+		"settings-ssh":          true,
 		"restr-gui":             true,
 		"restrictions":          consent.WebRestrictions{},
 		"capabilities":          consent.AllWebCapabilities(),
 		"subtoken-capabilities": consent.AllWebCapabilities(),
 	}
-	return ctx.Render("sites/ssh", binding, layoutMain)
+	return ctx.Render("sites/settings", binding, layoutMain)
 }
 
 func handleNativeCallback(ctx *fiber.Ctx) error {
diff --git a/internal/server/server.go b/internal/server/server.go
index b7e5aca6..cae020ea 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -105,7 +105,6 @@ func addRoutes(s fiber.Router) {
 	s.Get("/native/abort", handleNativeConsentAbortCallback)
 	s.Get(routes.GetGeneralPaths().Privacy, handlePrivacy)
 	s.Get("/settings", handleSettings)
-	s.Get("/settings/grants/ssh", handleSSH)
 	addAPIRoutes(s)
 }
 
diff --git a/internal/server/web/partials/grants.mustache b/internal/server/web/partials/grants.mustache
index aceddaec..76807f47 100644
--- a/internal/server/web/partials/grants.mustache
+++ b/internal/server/web/partials/grants.mustache
@@ -1,71 +1,71 @@
 <div id="settings-grants">
-    <table class="table table-striped bg-my_grey">
-        <thead>
-        <tr>
-            <th>Grant Type</th>
-            <th>Description</th>
-            <th style="width: 50px;">Enabled</th>
-        </tr>
-        </thead>
-        <tbody id="grants-table">
+    <table class="table table-secondary">
+        <tbody>
         {{#grants}}
             <tr>
-                <td>{{DisplayName}}
-                    <a class="float-right text-primary" href="{{Link}}" data-toggle="tooltip" data-placement="top"
-                       title="Grant Type Settings">
-                        <i class="fas fa-cog"></i>
-                    </a></td>
-                <td>{{Description}}</td>
-                <td class="text-center">
-                    <div class="form-group">
-                        <div class="custom-control custom-switch">
-                            <input type="checkbox" class="custom-control-input grant-enable"
-                                   aria-describedby="{{Name}}-GrantHelp" id="{{Name}}-GrantEnable" name="{{Name}}">
-                            <label class="custom-control-label" for="{{Name}}-GrantEnable"><span class="sr-only"></span></label>
-                        </div>
-                    </div>
+                <td style="width: 10%;"><h3 class="d-flex justify-content-between">{{DisplayName}}</h3></td>
+                <td class="smaller-lead">{{Description}}</td>
+                <td style="width: 1%;" class="text-center">
+                        <span class="custom-control custom-switch">
+            <input type="checkbox" class="custom-control-input grant-enable"
+                   aria-describedby="{{Name}}-GrantHelp" id="{{Name}}-GrantEnable" name="{{Name}}">
+            <label class="custom-control-label" for="{{Name}}-GrantEnable">Enabled</label>
+                        </span>
+                </td>
+                <td style="width: 11%;" class="text-right">
+                    <button style="width: 100%;" class="btn btn-light my-expand" type="button" data-toggle="collapse"
+                            data-target="#settings-{{Name}}-body" aria-expanded="false"
+                            aria-controls="settings-{{Name}}-body">Expand
+                    </button>
                 </td>
             </tr>
+            <tr class="collapse no-top-border" id="settings-{{Name}}-body">
+                <td class="no-top-border"></td>
+                <td class="no-top-border" colspan="3">
 
-            <div class="modal fade" tabindex="-1" role="dialog" id="{{Name}}-grantEnableModal">
-                <div class="modal-dialog modal-dialog-centered" role="document">
-                    <div class="modal-content">
-                        <div class="modal-header">
-                            <h5 class="modal-title">Enable {{DisplayName}}</h5>
-                            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
-                                <span aria-hidden="true">&times;</span>
-                            </button>
-                        </div>
-                        <div class="modal-body">Confirm to enable the '{{DisplayName}}' Grant Type.</div>
-                        <div class="modal-footer">
-                            <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
-                            <button type="button" class="btn btn-primary" data-dismiss="modal"
-                                    onclick="enableGrant('{{Name}}')">Enable
-                            </button>
+                    <div>{{{EmbedBody}}}</div>
+
+                    <div class="modal fade" tabindex="-1" role="dialog" id="{{Name}}-grantEnableModal">
+                        <div class="modal-dialog modal-dialog-centered" role="document">
+                            <div class="modal-content">
+                                <div class="modal-header">
+                                    <h5 class="modal-title">Enable {{DisplayName}}</h5>
+                                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                                        <span aria-hidden="true">&times;</span>
+                                    </button>
+                                </div>
+                                <div class="modal-body">Confirm to enable the '{{DisplayName}}' Grant Type.</div>
+                                <div class="modal-footer">
+                                    <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
+                                    <button type="button" class="btn btn-primary" data-dismiss="modal"
+                                            onclick="enableGrant('{{Name}}')">Enable
+                                    </button>
+                                </div>
+                            </div>
                         </div>
                     </div>
-                </div>
-            </div>
 
-            <div class="modal fade" tabindex="-1" role="dialog" id="{{Name}}-grantDisableModal">
-                <div class="modal-dialog modal-dialog-centered" role="document">
-                    <div class="modal-content">
-                        <div class="modal-header">
-                            <h5 class="modal-title">Disable {{DisplayName}}</h5>
-                            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
-                                <span aria-hidden="true">&times;</span>
-                            </button>
-                        </div>
-                        <div class="modal-body">Confirm to disable the '{{DisplayName}}' Grant Type.</div>
-                        <div class="modal-footer">
-                            <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
-                            <button type="button" class="btn btn-primary" data-dismiss="modal"
-                                    onclick="disableGrant('{{Name}}')">Disable
-                            </button>
+                    <div class="modal fade" tabindex="-1" role="dialog" id="{{Name}}-grantDisableModal">
+                        <div class="modal-dialog modal-dialog-centered" role="document">
+                            <div class="modal-content">
+                                <div class="modal-header">
+                                    <h5 class="modal-title">Disable {{DisplayName}}</h5>
+                                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                                        <span aria-hidden="true">&times;</span>
+                                    </button>
+                                </div>
+                                <div class="modal-body">Confirm to disable the '{{DisplayName}}' Grant Type.</div>
+                                <div class="modal-footer">
+                                    <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
+                                    <button type="button" class="btn btn-primary" data-dismiss="modal"
+                                            onclick="disableGrant('{{Name}}')">Disable
+                                    </button>
+                                </div>
+                            </div>
                         </div>
                     </div>
-                </div>
-            </div>
+                </td>
+            </tr>
         {{/grants}}
         </tbody>
     </table>
diff --git a/internal/server/web/partials/scripts.mustache b/internal/server/web/partials/scripts.mustache
index c7c7b1a5..df58c3c0 100644
--- a/internal/server/web/partials/scripts.mustache
+++ b/internal/server/web/partials/scripts.mustache
@@ -31,11 +31,8 @@
     {{/settings}}
     {{#settings-ssh}}
         <script src="/static/js/lib/clipboard.min.js"></script>
-        <script src="/static/js/tokeninfo.js"></script>
-        <script src="/static/js/mt-helper.js"></script>
-        <script src="/static/js/settings-helper.js"></script>
-        <script src="/static/js/ssh.js"></script>
         <script src="/static/js/lib/behave.min.js"></script>
+        <script src="/static/js/ssh.js"></script>
         <script src="/static/js/restrictions.js"></script>
         <script src="/static/js/capabilities.js"></script>
     {{/settings-ssh}}
diff --git a/internal/server/web/sites/ssh.mustache b/internal/server/web/sites/settings-ssh.mustache
similarity index 77%
rename from internal/server/web/sites/ssh.mustache
rename to internal/server/web/sites/settings-ssh.mustache
index dc205874..c6877756 100644
--- a/internal/server/web/sites/ssh.mustache
+++ b/internal/server/web/sites/settings-ssh.mustache
@@ -1,7 +1,3 @@
-<h3>SSH Grant Type</h3>
-<p>
-    Below you can find information about your ssh keys usable with this grant type. You can also add or remove ssh keys.
-</p>
 <p>
     To add a new ssh key you have to configure what can be done with this ssh key (capabilities) and upload the ssh key.
     You can also define restrictions. Then you have to do the OIDC authorization code flow and authenticate against the
@@ -14,8 +10,8 @@
         <div id="sshGrantStatusEnabled" class="d-none">
             <h4>
                 SSH Grant is Enabled
-                <a href="#" role="button" class="hover-item" onclick="$('#disableModal').modal()" data-toggle="tooltip"
-                   data-placement="top" title="Disable SSH Grant">
+                <a href="#" role="button" class="hover-item" onclick="$('#ssh-grantDisableModal').modal()"
+                   data-toggle="tooltip" data-placement="top" title="Disable SSH Grant">
                     <i class="fas fa-times-circle text-danger hover-text"></i>
                     <i class="fas fa-check-circle text-success non-hover-text"></i>
                 </a>
@@ -24,54 +20,13 @@
         <div id="sshGrantStatusDisabled" class="d-none">
             <h4>
                 SSH Grant is Disabled
-                <a href="#" role="button" class="hover-item" onclick="$('#enableModal').modal()" data-toggle="tooltip"
-                   data-placement="top" title="Enable SSH Grant">
+                <a href="#" role="button" class="hover-item" onclick="$('#ssh-grantEnableModal').modal()"
+                   data-toggle="tooltip" data-placement="top" title="Enable SSH Grant">
                     <i class="fas fa-check-circle text-success hover-text"></i>
                     <i class="fas fa-times-circle text-danger non-hover-text"></i>
                 </a>
             </h4>
         </div>
-
-
-        <div class="modal fade" tabindex="-1" role="dialog" id="enableModal">
-            <div class="modal-dialog modal-dialog-centered" role="document">
-                <div class="modal-content">
-                    <div class="modal-header">
-                        <h5 class="modal-title">Enable SSH Grant</h5>
-                        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
-                            <span aria-hidden="true">&times;</span>
-                        </button>
-                    </div>
-                    <div class="modal-body">Confirm to enable the 'SSH' Grant Type.</div>
-                    <div class="modal-footer">
-                        <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
-                        <button type="button" class="btn btn-primary" data-dismiss="modal" onclick="enableSSH()">
-                            Enable
-                        </button>
-                    </div>
-                </div>
-            </div>
-        </div>
-
-        <div class="modal fade" tabindex="-1" role="dialog" id="disableModal">
-            <div class="modal-dialog modal-dialog-centered" role="document">
-                <div class="modal-content">
-                    <div class="modal-header">
-                        <h5 class="modal-title">Disable SSH Grant</h5>
-                        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
-                            <span aria-hidden="true">&times;</span>
-                        </button>
-                    </div>
-                    <div class="modal-body">Confirm to disable the 'SSH' Grant Type.</div>
-                    <div class="modal-footer">
-                        <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
-                        <button type="button" class="btn btn-primary" data-dismiss="modal" onclick="disableSSH()">
-                            Disable
-                        </button>
-                    </div>
-                </div>
-            </div>
-        </div>
     </div>
     <button type="button" class="btn btn-success" id="addSSHKeyBtn" onclick="$('#addModal').modal()"><i
             class="fas fa-plus-circle"></i> Add SSH Key
diff --git a/internal/server/web/static/css/mytoken.css b/internal/server/web/static/css/mytoken.css
index 4b251f39..6c9b8f85 100644
--- a/internal/server/web/static/css/mytoken.css
+++ b/internal/server/web/static/css/mytoken.css
@@ -50,4 +50,17 @@ ul.list-group li.list-group-item {
 
 .toggle.round, .toggle-on.round, .toggle-off.round {
     border-radius: 20rem;
+}
+
+hr {
+    border-top-color: rgba(223, 105, 26, 0.3);
+}
+
+.smaller-lead {
+    font-weight: 300;
+    font-size: 1.1rem;
+}
+
+.no-top-border {
+    border-top: none !important;
 }
\ No newline at end of file
diff --git a/internal/server/web/static/js/settings.js b/internal/server/web/static/js/settings.js
index d4b78137..85b9d8b7 100644
--- a/internal/server/web/static/js/settings.js
+++ b/internal/server/web/static/js/settings.js
@@ -2,6 +2,7 @@ $(function () {
     chainFunctions(
         checkIfLoggedIn,
         initGrants,
+        initSSH,
     );
     // https://stackoverflow.com/a/17552459
     // Javascript to enable link to tab
@@ -12,7 +13,7 @@ $(function () {
     }
 })
 
-function initGrants() {
+function initGrants(...next) {
     useSettingsToken(function (token) {
         $.ajax({
             type: "GET",
@@ -25,6 +26,7 @@ function initGrants() {
                 grants.forEach(function (grant) {
                     $('#' + grant['grant_type'] + '-GrantEnable').prop('checked', grant['enabled']);
                 })
+                doNext(...next);
             },
             error: function (errRes) {
                 $errorModalMsg.text(getErrorMessage(errRes));
@@ -48,11 +50,16 @@ $('.grant-enable').click(function () {
 function enableGrant(grant) {
     sendGrantRequest(grant, true, function () {
         $('#' + grant + '-GrantEnable').prop('checked', true);
+        enableGrantCallbacks[grant]();
     });
 }
 
 function disableGrant(grant) {
     sendGrantRequest(grant, false, function () {
         $('#' + grant + '-GrantEnable').prop('checked', false);
+        disableGrantCallbacks[grant]();
     });
-}
\ No newline at end of file
+}
+
+let enableGrantCallbacks = {};
+let disableGrantCallbacks = {};
diff --git a/internal/server/web/static/js/ssh.js b/internal/server/web/static/js/ssh.js
index 78aa5554..b2121812 100644
--- a/internal/server/web/static/js/ssh.js
+++ b/internal/server/web/static/js/ssh.js
@@ -29,14 +29,6 @@ clipboard.on('success', function (e) {
     el.attr('data-original-title', originalText);
 });
 
-$(function () {
-    chainFunctions(
-        checkIfLoggedIn,
-        initRestrGUI,
-        initSSH,
-    );
-})
-
 $('#addModal').on('hidden.bs.modal', function () {
     window.clearInterval(intervalID);
     $sshResult.hideB();
@@ -44,23 +36,20 @@ $('#addModal').on('hidden.bs.modal', function () {
     initSSH();
 })
 
-function enableSSH() {
-    sendGrantRequest('ssh', true, function () {
-        $sshGrantStatusEnabled.showB();
-        $sshGrantStatusDisabled.hideB();
-        $addSSHKeyBtn.prop('disabled', false);
-    });
-}
+enableGrantCallbacks['ssh'] = function enableSSHCallback() {
+    $sshGrantStatusEnabled.showB();
+    $sshGrantStatusDisabled.hideB();
+    $addSSHKeyBtn.prop('disabled', false);
+};
 
-function disableSSH() {
-    sendGrantRequest('ssh', false, function () {
-        $sshGrantStatusEnabled.hideB();
-        $sshGrantStatusDisabled.showB();
-        $addSSHKeyBtn.prop('disabled', true);
-    });
-}
+disableGrantCallbacks['ssh'] = function disableSSHCallback() {
+    $sshGrantStatusEnabled.hideB();
+    $sshGrantStatusDisabled.showB();
+    $addSSHKeyBtn.prop('disabled', true);
+};
 
-function initSSH() {
+function initSSH(...next) {
+    initRestrGUI();
     clearSSHKeyTable();
     useSettingsToken(function (token) {
         $.ajax({
@@ -83,6 +72,7 @@ function initSSH() {
                         addSSHKeyToTable(key);
                     })
                 }
+                doNext(...next);
             },
             error: function (errRes) {
                 $errorModalMsg.text(getErrorMessage(errRes));
-- 
GitLab