~jan0sch/smederee

Showing details for patch 4d12aa1b8e9a98244da555a20e975421606c096c.
2022-10-25 (Tue), 3:26 PM - Jens Grassel - 4d12aa1b8e9a98244da555a20e975421606c096c

SSH: Key-Management

- add functionality to delete keys
- minor fixes
- some HTML/CSS styling
Summary of changes
4 files modified with 79 lines added and 17 lines removed
  • modules/hub/src/main/resources/assets/css/main.css with 8 added and 0 removed lines
  • modules/hub/src/main/resources/messages_en.properties with 4 added and 1 removed lines
  • modules/hub/src/main/scala/de/smederee/hub/AccountManagementRoutes.scala with 37 added and 1 removed lines
  • modules/hub/src/main/twirl/de/smederee/hub/views/account/sshSettings.scala.html with 30 added and 15 removed lines
diff -rN -u old-smederee/modules/hub/src/main/resources/assets/css/main.css new-smederee/modules/hub/src/main/resources/assets/css/main.css
--- old-smederee/modules/hub/src/main/resources/assets/css/main.css	2025-02-01 21:54:00.179323407 +0000
+++ new-smederee/modules/hub/src/main/resources/assets/css/main.css	2025-02-01 21:54:00.183323417 +0000
@@ -13,10 +13,18 @@
   padding: 0px 10px;
 }
 
+.ssh-key-item {
+  padding: 10px 5px 10px 5px;
+}
+
 .ssh-key-item-icon {
   margin-right: 5px;
 }
 
+.ssh-key-comment {
+  word-wrap: break-word;
+}
+
 .validate-email-form {
   border: 1px solid black;
   padding: 0px 10px;
diff -rN -u old-smederee/modules/hub/src/main/resources/messages_en.properties new-smederee/modules/hub/src/main/resources/messages_en.properties
--- old-smederee/modules/hub/src/main/resources/messages_en.properties	2025-02-01 21:54:00.179323407 +0000
+++ new-smederee/modules/hub/src/main/resources/messages_en.properties	2025-02-01 21:54:00.183323417 +0000
@@ -69,6 +69,8 @@
 form.ssh.add.key.placeholder=The content of your key file (usually located in ~/.ssh/id_rsa.pub) starting with ssh-rsa or ssh-ed25519.
 form.ssh.add.name=Name
 form.ssh.add.name.help=If left empty it will be taken from the ssh key comment (if that exists).
+form.ssh.delete.button.submit=Delete key
+form.ssh.delete.i-am-sure=Yes, I'm sure!
 
 # Global / generic translations
 global.alpha=Alpha Notice
@@ -189,6 +191,7 @@
 user.settings.ssh.description=Here you can manage your SSH keys.
 user.settings.ssh.key.created=Uploaded on {0,date,yyyy-MM-dd (E)}
 user.settings.ssh.key.last-used=Last used on {0,date,yyyy-MM-dd (E)}
+user.settings.ssh.list.title=Manage your existing ssh keys.
+user.settings.ssh.list.empty=You haven''t uploaded any ssh keys yet.
 user.settings.ssh.title=SSH-Keys
 user.settings.title=Settings
-
diff -rN -u old-smederee/modules/hub/src/main/scala/de/smederee/hub/AccountManagementRoutes.scala new-smederee/modules/hub/src/main/scala/de/smederee/hub/AccountManagementRoutes.scala
--- old-smederee/modules/hub/src/main/scala/de/smederee/hub/AccountManagementRoutes.scala	2025-02-01 21:54:00.179323407 +0000
+++ new-smederee/modules/hub/src/main/scala/de/smederee/hub/AccountManagementRoutes.scala	2025-02-01 21:54:00.183323417 +0000
@@ -40,6 +40,8 @@
 import org.http4s.twirl.TwirlInstances._
 import org.slf4j.LoggerFactory
 
+import scala.util.Try
+
 /** Routes for user account self management which should provide every functionality needed by users to manage their
   * account.
   *
@@ -261,6 +263,40 @@
       }
   }
 
+  private val deleteSshKey: AuthedRoutes[Account, F] = AuthedRoutes.of {
+    case ar @ POST -> Root / "user" / "settings" / "ssh" / "delete" as user =>
+      ar.req.decodeStrict[F, UrlForm] { urlForm =>
+        for {
+          csrf <- Sync[F].delay(ar.req.getCsrfToken)
+          formData <- Sync[F].delay {
+            urlForm.values.map { t =>
+              val (key, values) = t
+              (
+                key,
+                values.headOption.getOrElse("")
+              ) // Pick the first value (a field might get submitted multiple times)!
+            }
+          }
+          actionBaseUri <- Sync[F].delay(configuration.external.createFullUri(uri"user/settings"))
+          userIsSure    <- Sync[F].delay(formData.get("i-am-sure").exists(_ === "yes"))
+          sshKeyId <- Sync[F].delay {
+            Try(formData.get("ssh-key-id").map(UUID.fromString)) match {
+              case scala.util.Failure(error) =>
+                log.error("Error while parsing ssh key id upon key delete request.", error)
+                None
+              case scala.util.Success(Some(keyId)) => Option(keyId)
+              case _                               => None
+            }
+          }
+          _ <- userIsSure match {
+            case false => Sync[F].pure(None)
+            case true  => sshKeyId.traverse(id => accountManagementRepo.deleteSshKey(id, user.uid))
+          }
+          resp <- SeeOther(Location(actionBaseUri.addSegment("ssh")))
+        } yield resp
+      }
+  }
+
   private val sendValidationEmail: AuthedRoutes[Account, F] = AuthedRoutes.of {
     case ar @ POST -> Root / "user" / "settings" / "email" / "validate" as user =>
       for {
@@ -334,7 +370,7 @@
   }
 
   val protectedRoutes =
-    addSshKey <+> deleteAccount <+> sendValidationEmail <+> showAccountSettings <+> showAccountSshSettings
+    addSshKey <+> deleteAccount <+> deleteSshKey <+> sendValidationEmail <+> showAccountSettings <+> showAccountSshSettings
 
   val routes = validateEmailAddress
 
diff -rN -u old-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/account/sshSettings.scala.html new-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/account/sshSettings.scala.html
--- old-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/account/sshSettings.scala.html	2025-02-01 21:54:00.179323407 +0000
+++ new-smederee/modules/hub/src/main/twirl/de/smederee/hub/views/account/sshSettings.scala.html	2025-02-01 21:54:00.183323417 +0000
@@ -47,7 +47,7 @@
               </div>
               <div class="pure-control-group">
                 <label for="@{fieldKey}">@Messages("form.ssh.add.key")</label>
-                <textarea class="pure-input-1" id="@{fieldKey}" name="@{fieldKey}" placeholder="@Messages("form.ssh.add.key.placeholder")" maxlength="4096" rows="8">@{formData.get(fieldKey)}</textarea>
+                <textarea class="pure-input-1" id="@{fieldKey}" name="@{fieldKey}" placeholder="@Messages("form.ssh.add.key.placeholder")" maxlength="4096" rows="8" required="">@{formData.get(fieldKey)}</textarea>
                 <span class="pure-form-message" id="@{fieldKey}.help"><strong>@Messages("form.ssh.add.key.help")</strong></span>
                 @renderFormErrors(fieldKey, formErrors)
               </div>
@@ -64,21 +64,36 @@
     <div class="pure-u-1-1 pure-u-md-1-1">
       <div class="l-box">
         <div class="ssh-key-list">
-        @for(key <- keys) {
-          <div class="ssh-key-item">
-            <div class="ssh-key-item-icon left-floated">
-              <i class="fa-solid fa-key fa-3x"></i>
-            </div>
-            <div class="ssh-key-item-details">
-              <strong>@key.comment</strong>
-              <pre>SHA256:@key.fingerprint</pre>
-              <div class="ssh-key-item-timestamps">
-                @Messages("user.settings.ssh.key.created", java.util.Date.from(key.createdAt.toInstant)) @key.lastUsedAt.map(timestamp => Messages("user.settings.ssh.key.last-used", java.util.Date.from(timestamp.toInstant)))
+          <h4>@Messages("user.settings.ssh.list.title")</h4>
+          @if(keys.size === 0) {
+            <div class="alert alert-info">@Messages("user.settings.ssh.list.empty")</div>
+          } else {
+            @for(key <- keys) {
+              <div class="ssh-key-item">
+                <div class="ssh-key-item-icon left-floated">
+                  <i class="fa-solid fa-key fa-3x"></i>
+                </div>
+                <div class="ssh-key-item-form right-floated pure-u-1-3">
+                  <form action="@deleteAction" class="pure-form" method="POST" accept-charset="UTF-8">
+                    <fieldset>
+                      <input type="hidden" id="ssh-key-id-@key.id" name="ssh-key-id" readonly="" value="@key.id">
+                      <label for="i-am-sure-@key.id"><input id="i-am-sure-@key.id" name="i-am-sure" required="" type="checkbox" value="yes"/> @Messages("form.ssh.delete.i-am-sure")</label>
+                      @csrfToken(csrf)
+                      <button type="submit" class="button-warning pure-button">@Messages("form.ssh.delete.button.submit")</button>
+                    </fieldset>
+                  </form>
+                </div>
+                <div class="ssh-key-item-details">
+                  <div class="ssh-key-comment"><strong>@key.comment</strong></div>
+                  <div class="ssh-key-fingerprint">SHA256:@key.fingerprint</div>
+                  <div class="ssh-key-item-timestamps">
+                    @Messages("user.settings.ssh.key.created", java.util.Date.from(key.createdAt.toInstant)) @key.lastUsedAt.map(timestamp => Messages("user.settings.ssh.key.last-used", java.util.Date.from(timestamp.toInstant)))
+                  </div>
+                </div>
+                <div class="clearfix"></div>
               </div>
-            </div>
-            <div class="clearfix"></div>
-          </div>
-        }
+            }
+          }
         </div>
       </div>
     </div>