Move UUID validation from multiple places into a middleware func

This commit is contained in:
Kailash Nadh 2019-07-21 20:11:11 +05:30
parent 3fddd78ebf
commit a060d94cb5
2 changed files with 32 additions and 42 deletions

View File

@ -4,6 +4,7 @@ import (
"encoding/json"
"net/http"
"net/url"
"regexp"
"strconv"
"strings"
@ -37,6 +38,8 @@ type pagination struct {
Limit int `json:"limit"`
}
var reUUID = regexp.MustCompile("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$")
// registerHandlers registers HTTP handlers.
func registerHandlers(e *echo.Echo) {
e.GET("/", handleIndexPage)
@ -97,12 +100,18 @@ func registerHandlers(e *echo.Echo) {
e.DELETE("/api/templates/:id", handleDeleteTemplate)
// Subscriber facing views.
e.GET("/subscription/:campUUID/:subUUID", handleSubscriptionPage)
e.POST("/subscription/:campUUID/:subUUID", handleSubscriptionPage)
e.POST("/subscription/export/:subUUID", handleSelfExportSubscriberData)
e.POST("/subscription/wipe/:subUUID", handleWipeSubscriberData)
e.GET("/link/:linkUUID/:campUUID/:subUUID", handleLinkRedirect)
e.GET("/campaign/:campUUID/:subUUID/px.png", handleRegisterCampaignView)
e.GET("/subscription/:campUUID/:subUUID", validateUUID(handleSubscriptionPage,
"campUUID", "subUUID"))
e.POST("/subscription/:campUUID/:subUUID", validateUUID(handleSubscriptionPage,
"campUUID", "subUUID"))
e.POST("/subscription/export/:subUUID", validateUUID(handleSelfExportSubscriberData,
"subUUID"))
e.POST("/subscription/wipe/:subUUID", validateUUID(handleWipeSubscriberData,
"subUUID"))
e.GET("/link/:linkUUID/:campUUID/:subUUID", validateUUID(handleLinkRedirect,
"linkUUID", "campUUID", "subUUID"))
e.GET("/campaign/:campUUID/:subUUID/px.png", validateUUID(handleRegisterCampaignView,
"campUUID", "subUUID"))
// Static views.
e.GET("/lists", handleIndexPage)
@ -130,6 +139,20 @@ func handleIndexPage(c echo.Context) error {
return c.String(http.StatusOK, string(b))
}
// validateUUID validates the UUID string format for a given set of params.
func validateUUID(next echo.HandlerFunc, params ...string) echo.HandlerFunc {
return func(c echo.Context) error {
for _, p := range params {
if !reUUID.MatchString(c.Param(p)) {
return c.Render(http.StatusBadRequest, "message",
makeMsgTpl("Invalid request", "",
`One or more UUIDs in the request are invalid.`))
}
}
return next(c)
}
}
// makeAttribsBlob takes a list of keys and values and creates
// a JSON map out of them.
func makeAttribsBlob(keys []string, vals []string) ([]byte, bool) {
@ -154,7 +177,6 @@ func makeAttribsBlob(keys []string, vals []string) ([]byte, bool) {
val = s
}
}
attribs[key] = val
}
@ -162,7 +184,6 @@ func makeAttribsBlob(keys []string, vals []string) ([]byte, bool) {
j, _ := json.Marshal(attribs)
return j, true
}
return nil, false
}

View File

@ -7,7 +7,6 @@ import (
"image/png"
"io"
"net/http"
"regexp"
"strconv"
"github.com/knadh/listmonk/messenger"
@ -52,8 +51,7 @@ type msgTpl struct {
}
var (
regexValidUUID = regexp.MustCompile("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$")
pixelPNG = drawTransparentImage(3, 14)
pixelPNG = drawTransparentImage(3, 14)
)
// Render executes and renders a template for echo.
@ -83,14 +81,6 @@ func handleSubscriptionPage(c echo.Context) error {
out.AllowExport = app.Constants.Privacy.AllowExport
out.AllowWipe = app.Constants.Privacy.AllowWipe
if !regexValidUUID.MatchString(campUUID) ||
!regexValidUUID.MatchString(subUUID) {
return c.Render(http.StatusBadRequest, "message",
makeMsgTpl("Invalid request", "",
`The unsubscription request contains invalid IDs.
Please follow the correct link.`))
}
// Unsubscribe.
if unsub {
// Is blacklisting allowed?
@ -119,12 +109,6 @@ func handleLinkRedirect(c echo.Context) error {
campUUID = c.Param("campUUID")
subUUID = c.Param("subUUID")
)
if !regexValidUUID.MatchString(linkUUID) ||
!regexValidUUID.MatchString(campUUID) ||
!regexValidUUID.MatchString(subUUID) {
return c.Render(http.StatusBadRequest, "message",
makeMsgTpl("Invalid link", "", "The link you clicked is invalid."))
}
var url string
if err := app.Queries.RegisterLinkClick.Get(&url, linkUUID, campUUID, subUUID); err != nil {
@ -146,13 +130,9 @@ func handleRegisterCampaignView(c echo.Context) error {
campUUID = c.Param("campUUID")
subUUID = c.Param("subUUID")
)
if regexValidUUID.MatchString(campUUID) &&
regexValidUUID.MatchString(subUUID) {
if _, err := app.Queries.RegisterCampaignView.Exec(campUUID, subUUID); err != nil {
app.Logger.Printf("error registering campaign view: %s", err)
}
if _, err := app.Queries.RegisterCampaignView.Exec(campUUID, subUUID); err != nil {
app.Logger.Printf("error registering campaign view: %s", err)
}
c.Response().Header().Set("Cache-Control", "no-cache")
return c.Blob(http.StatusOK, "image/png", pixelPNG)
}
@ -166,12 +146,6 @@ func handleSelfExportSubscriberData(c echo.Context) error {
app = c.Get("app").(*App)
subUUID = c.Param("subUUID")
)
if !regexValidUUID.MatchString(subUUID) {
return c.Render(http.StatusInternalServerError, "message",
makeMsgTpl("Invalid request", "",
"The subscriber ID is invalid."))
}
// Is export allowed?
if !app.Constants.Privacy.AllowExport {
return c.Render(http.StatusBadRequest, "message",
@ -230,11 +204,6 @@ func handleWipeSubscriberData(c echo.Context) error {
app = c.Get("app").(*App)
subUUID = c.Param("subUUID")
)
if !regexValidUUID.MatchString(subUUID) {
return c.Render(http.StatusInternalServerError, "message",
makeMsgTpl("Invalid request", "",
"The subscriber ID is invalid."))
}
// Is wiping allowed?
if !app.Constants.Privacy.AllowExport {