From c2e868f1801b0bdddaa686e13435ab706aef2522 Mon Sep 17 00:00:00 2001 From: Sergio Vera Date: Fri, 31 May 2024 14:41:59 +0200 Subject: [PATCH 1/5] Enhanced authorization middleware --- internal/webserver/embedded/js/send-email.js | 15 ++++++++++----- .../webserver/embedded/translations/es.yml | 3 ++- .../webserver/embedded/translations/fr.yml | 3 ++- .../webserver/embedded/views/document.html | 14 +++++++------- internal/webserver/embedded/views/layout.html | 19 ++++++++++++++----- .../embedded/views/partials/actions.html | 14 +++++++------- internal/webserver/middleware.go | 16 +++++++++++----- internal/webserver/routes.go | 4 ++-- 8 files changed, 55 insertions(+), 33 deletions(-) diff --git a/internal/webserver/embedded/js/send-email.js b/internal/webserver/embedded/js/send-email.js index 68bb5b5..632df07 100644 --- a/internal/webserver/embedded/js/send-email.js +++ b/internal/webserver/embedded/js/send-email.js @@ -9,7 +9,7 @@ Array.from(forms).forEach(form => { const submit = form.querySelector('button'); let sendIcon = form.querySelector('.bi-send-fill'); let spinner = form.querySelector('.spinner-border'); - + submit.setAttribute("disabled", true); spinner.classList.remove("visually-hidden"); sendIcon.classList.add("visually-hidden"); @@ -20,16 +20,21 @@ Array.from(forms).forEach(form => { }, body: new URLSearchParams({ 'email': form.elements[0].value, - 'slug': form.elements[1].value, + 'slug': form.elements[1].value, }) }) .then((response) => { let message = form.querySelector(".send-email-message") + const lang = form.getAttribute("data-lang") message.classList.remove("visually-hidden"); if (!response.ok) { - message.innerHTML = form.getAttribute("data-error-message"); - message.classList.remove("text-success"); - message.classList.add("text-danger"); + if (response.status == "403") { + location.reload() + } else { + message.innerHTML = form.getAttribute("data-error-message"); + message.classList.remove("text-success"); + message.classList.add("text-danger"); + } } else { message.innerHTML = form.getAttribute("data-success-message"); message.classList.remove("text-danger"); diff --git a/internal/webserver/embedded/translations/es.yml b/internal/webserver/embedded/translations/es.yml index fb5783a..02caf83 100644 --- a/internal/webserver/embedded/translations/es.yml +++ b/internal/webserver/embedded/translations/es.yml @@ -21,7 +21,7 @@ "Send": Enviar "Actions": Acciones "Send to email unavailable, no email service configured": "Enviar a correo electrónico no disponible, no se ha configurado ningún servicio" -"Document sent succesfully": "Documento enviado con éxito" +"Document sent successfully": "Documento enviado con éxito" "There was an error sending the document, please try again later": "Hubo un error al enviar el documento, por favor, vuelva a intentarlo más tarde" "Read": "Leer" "Users": "Usuarios" @@ -132,3 +132,4 @@ "Show settings": "Mostrar configuración" "Go left": "Ir a la izquierda" "Go right": "Ir a la derecha" +"Session expired, please log in again.": "Sesión expirada, por favor identifícate de nuevo." diff --git a/internal/webserver/embedded/translations/fr.yml b/internal/webserver/embedded/translations/fr.yml index 8bcd1c0..7a71638 100644 --- a/internal/webserver/embedded/translations/fr.yml +++ b/internal/webserver/embedded/translations/fr.yml @@ -21,7 +21,7 @@ "Send": Envoyer "Actions": Actions "Send to email unavailable, no email service configured": "Envoyer par e-mail indisponible, aucun service de messagerie configuré" -"Document sent succesfully": "Document envoyé avec succès" +"Document sent successfully": "Document envoyé avec succès" "There was an error sending the document, please try again later": "Une erreur s'est produite lors de l'envoi du document, veuillez réessayer ultérieurement" "Read": "Lire" "Users": "Utilisateurs" @@ -132,3 +132,4 @@ "Show settings": "Afficher les paramètres" "Go left": "Aller à gauche" "Go right": "Aller à droite" +"Session expired, please log in again.": "Session expirée, veuillez vous reconnecter." diff --git a/internal/webserver/embedded/views/document.html b/internal/webserver/embedded/views/document.html index 8445f7c..b1df364 100644 --- a/internal/webserver/embedded/views/document.html +++ b/internal/webserver/embedded/views/document.html @@ -30,7 +30,7 @@

  {{t .Lang "Read"}} - + @@ -47,20 +47,20 @@

  {{t .Lang "Highlight"}} - +   {{t .Lang "Remove from highlights"}} - {{end}} + {{end}} {{if not .EmailSendingConfigured}}

{{t .Lang "Send to email unavailable, no email service configured"}}

{{else}}
-
+
- {{end}} + {{end}} {{ $length := len .SameAuthors }} {{ if gt $length 0 }}
@@ -178,7 +178,7 @@

{{end}}

- {{end}} + {{end}} {{ $length := len .SameSubjects }} {{ if gt $length 0 }}
@@ -198,7 +198,7 @@

{{t $lang "Other documents with similar subjects"}}

{{end}}
- {{end}} + {{end}} diff --git a/internal/webserver/embedded/views/layout.html b/internal/webserver/embedded/views/layout.html index 906d1c4..d8c57ed 100644 --- a/internal/webserver/embedded/views/layout.html +++ b/internal/webserver/embedded/views/layout.html @@ -20,6 +20,7 @@ + @@ -76,7 +77,7 @@

Coreander
- + {{t .Lang "Highlights"}} @@ -85,11 +86,11 @@

Coreander
- + {{t .Lang "Upload document"}} - - {{end}} + + {{end}}
  • @@ -144,7 +145,7 @@
    Coreander
    {{end}} - + {{if .Error}}
    {{end}} + {{if .Warning}} +
    + +
    + {{end}} + {{if .Message}}
    diff --git a/internal/webserver/middleware.go b/internal/webserver/middleware.go index 5f4fe25..ca0d96a 100644 --- a/internal/webserver/middleware.go +++ b/internal/webserver/middleware.go @@ -81,7 +81,10 @@ func AlwaysRequireAuthentication(jwtSecret []byte, sender Sender) func(*fiber.Ct return c.Next() }, ErrorHandler: func(c *fiber.Ctx, err error) error { - return forbidden(c, sender) + if err.Error() == "missing or malformed JWT" { + return forbidden(c, sender, "") + } + return forbidden(c, sender, "Session expired, please log in again.") }, }) } @@ -97,16 +100,18 @@ func ConfigurableAuthentication(jwtSecret []byte, sender Sender, requireAuth boo return c.Next() }, ErrorHandler: func(c *fiber.Ctx, err error) error { - err = c.Next() if requireAuth { - return forbidden(c, sender) + if err.Error() == "missing or malformed JWT" { + return forbidden(c, sender, "") + } + return forbidden(c, sender, "Session expired, please log in again.") } - return err + return c.Next() }, }) } -func forbidden(c *fiber.Ctx, sender Sender) error { +func forbidden(c *fiber.Ctx, sender Sender, message string) error { emailSendingConfigured := true if _, ok := sender.(*infrastructure.NoEmail); ok { emailSendingConfigured = false @@ -117,5 +122,6 @@ func forbidden(c *fiber.Ctx, sender Sender) error { "Version": c.App().Config().AppName, "EmailSendingConfigured": emailSendingConfigured, "DisableLoginLink": true, + "Warning": message, }, "layout") } diff --git a/internal/webserver/routes.go b/internal/webserver/routes.go index 011e71b..ebb1535 100644 --- a/internal/webserver/routes.go +++ b/internal/webserver/routes.go @@ -69,11 +69,11 @@ func routes(app *fiber.App, controllers Controllers, jwtSecret []byte, sender Se langGroup.Get("/upload", alwaysRequireAuthentication, RequireAdmin, controllers.Documents.UploadForm) langGroup.Post("/upload", alwaysRequireAuthentication, RequireAdmin, controllers.Documents.Upload) + langGroup.Get("/logout", alwaysRequireAuthentication, controllers.Auth.SignOut) + // Authentication requirement is configurable for all routes below this middleware app.Use(configurableAuthentication) - langGroup.Get("/logout", controllers.Auth.SignOut) - app.Get("/cover/:slug", controllers.Documents.Cover) langGroup.Get("/document/:slug", controllers.Documents.Detail) From b2c90ed9bf1e98dbc2450a42115dc27fde6e2e60 Mon Sep 17 00:00:00 2001 From: Sergio Vera Date: Fri, 31 May 2024 14:46:41 +0200 Subject: [PATCH 2/5] Removed htmx --- internal/webserver/embedded/views/layout.html | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/webserver/embedded/views/layout.html b/internal/webserver/embedded/views/layout.html index d8c57ed..d4d6aab 100644 --- a/internal/webserver/embedded/views/layout.html +++ b/internal/webserver/embedded/views/layout.html @@ -20,7 +20,6 @@ - From 024d11fceaf0db7db312c47e484c3f07430cbc62 Mon Sep 17 00:00:00 2001 From: Sergio Vera Date: Fri, 31 May 2024 14:47:44 +0200 Subject: [PATCH 3/5] Removed language const not used anymore --- internal/webserver/embedded/js/send-email.js | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/webserver/embedded/js/send-email.js b/internal/webserver/embedded/js/send-email.js index 632df07..73dd487 100644 --- a/internal/webserver/embedded/js/send-email.js +++ b/internal/webserver/embedded/js/send-email.js @@ -25,7 +25,6 @@ Array.from(forms).forEach(form => { }) .then((response) => { let message = form.querySelector(".send-email-message") - const lang = form.getAttribute("data-lang") message.classList.remove("visually-hidden"); if (!response.ok) { if (response.status == "403") { From e0ee82245bf4a11df463f5db9fa855072afa8a45 Mon Sep 17 00:00:00 2001 From: Sergio Vera Date: Fri, 31 May 2024 14:51:48 +0200 Subject: [PATCH 4/5] Simplified code --- internal/webserver/middleware.go | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/internal/webserver/middleware.go b/internal/webserver/middleware.go index ca0d96a..9c30671 100644 --- a/internal/webserver/middleware.go +++ b/internal/webserver/middleware.go @@ -81,10 +81,7 @@ func AlwaysRequireAuthentication(jwtSecret []byte, sender Sender) func(*fiber.Ct return c.Next() }, ErrorHandler: func(c *fiber.Ctx, err error) error { - if err.Error() == "missing or malformed JWT" { - return forbidden(c, sender, "") - } - return forbidden(c, sender, "Session expired, please log in again.") + return forbidden(c, sender, err) }, }) } @@ -101,21 +98,23 @@ func ConfigurableAuthentication(jwtSecret []byte, sender Sender, requireAuth boo }, ErrorHandler: func(c *fiber.Ctx, err error) error { if requireAuth { - if err.Error() == "missing or malformed JWT" { - return forbidden(c, sender, "") - } - return forbidden(c, sender, "Session expired, please log in again.") + return forbidden(c, sender, err) } return c.Next() }, }) } -func forbidden(c *fiber.Ctx, sender Sender, message string) error { +func forbidden(c *fiber.Ctx, sender Sender, err error) error { emailSendingConfigured := true if _, ok := sender.(*infrastructure.NoEmail); ok { emailSendingConfigured = false } + message := "" + if err.Error() != "missing or malformed JWT" { + message = "Session expired, please log in again." + } + return c.Status(fiber.StatusForbidden).Render("auth/login", fiber.Map{ "Lang": chooseBestLanguage(c), "Title": "Login", From 4a0a30eacb29aaf755305fced5d9fe6769667137 Mon Sep 17 00:00:00 2001 From: Sergio Vera Date: Fri, 31 May 2024 15:26:14 +0200 Subject: [PATCH 5/5] Expiration support for highlight and delete --- internal/webserver/embedded/js/delete.js | 4 ++-- internal/webserver/embedded/js/highlight.js | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/internal/webserver/embedded/js/delete.js b/internal/webserver/embedded/js/delete.js index ad40037..788eb76 100644 --- a/internal/webserver/embedded/js/delete.js +++ b/internal/webserver/embedded/js/delete.js @@ -31,11 +31,11 @@ deleteForm.addEventListener('submit', event => { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ - 'id': deleteForm.elements['id'].value, + 'id': deleteForm.elements['id'].value, }) }) .then((response) => { - if (response.ok) { + if (response.ok || response.status == "403") { location.reload(); } else { let message = document.getElementById("error-message-container"); diff --git a/internal/webserver/embedded/js/highlight.js b/internal/webserver/embedded/js/highlight.js index b278824..5393c15 100644 --- a/internal/webserver/embedded/js/highlight.js +++ b/internal/webserver/embedded/js/highlight.js @@ -37,6 +37,9 @@ Array.from(links).forEach((link) => { } return; } + if (response.status == "403") { + location.reload() + } }) .catch(function (error) { // Catch errors