From 41c59fd8e430587b37938108f1734a58e3794353 Mon Sep 17 00:00:00 2001 From: Peter Sanchez Date: Thu, 16 May 2024 19:26:30 -0600 Subject: [PATCH] Fixing validation misses in Slack command --- api/graph/schema.resolvers.go | 23 +++++----- slack/commands.go | 81 ++++++++++++++++++++++++++--------- 2 files changed, 72 insertions(+), 32 deletions(-) diff --git a/api/graph/schema.resolvers.go b/api/graph/schema.resolvers.go index 816c48c..152adde 100644 --- a/api/graph/schema.resolvers.go +++ b/api/graph/schema.resolvers.go @@ -1819,7 +1819,7 @@ func (r *mutationResolver) AddLinkShort(ctx context.Context, input *model.LinkSh } if input.ShortCode != nil { - validator.Expect(len(*input.ShortCode) < 20, + validator.Expect(len(*input.ShortCode) <= 20, lt.Translate("Short Code may not exceed 20 characters")). WithField("shortCode"). WithCode(valid.ErrValidationCode) @@ -1908,10 +1908,12 @@ func (r *mutationResolver) AddLinkShort(ctx context.Context, input *model.LinkSh } // Validate custom short code - if input.ShortCode != nil { + var shortCode string + if input.ShortCode != nil && *input.ShortCode != "" { + shortCode = links.SlugifyCase(*input.ShortCode) opts := &database.FilterOptions{ Filter: sq.And{ - sq.Eq{"l.short_code": *input.ShortCode}, + sq.Eq{"l.short_code": shortCode}, sq.Eq{"l.domain_id": input.DomainID}, }, Limit: 1, @@ -1931,16 +1933,13 @@ func (r *mutationResolver) AddLinkShort(ctx context.Context, input *model.LinkSh } linkShort := &models.LinkShort{ - URL: input.URL, - OrgID: org.ID, - UserID: int(user.ID), - DomainID: input.DomainID, + URL: input.URL, + OrgID: org.ID, + UserID: int(user.ID), + DomainID: input.DomainID, + ShortCode: shortCode, // if "", it will be generated before saving } - if input.ShortCode != nil && *input.ShortCode != "" { - shortCode := links.SlugifyCase(*input.ShortCode) - linkShort.ShortCode = shortCode - } if input.Title != nil { linkShort.Title = *input.Title } @@ -2001,7 +2000,7 @@ func (r *mutationResolver) UpdateLinkShort(ctx context.Context, input *model.Upd } if input.ShortCode != nil { - validator.Expect(len(*input.ShortCode) < 20, + validator.Expect(len(*input.ShortCode) <= 20, lt.Translate("Short Code may not exceed 20 characters")). WithField("shortCode"). WithCode(valid.ErrValidationCode) diff --git a/slack/commands.go b/slack/commands.go index be9fe9d..884a33e 100644 --- a/slack/commands.go +++ b/slack/commands.go @@ -7,13 +7,16 @@ import ( "links/internal/localizer" "links/models" "net/http" + "net/url" "strings" "git.sr.ht/~emersion/gqlclient" sq "github.com/Masterminds/squirrel" "github.com/labstack/echo/v4" + "golang.org/x/exp/slices" "netlandish.com/x/gobwebs/auth" "netlandish.com/x/gobwebs/database" + "netlandish.com/x/gobwebs/server" ) type SlackBlock struct { @@ -30,11 +33,12 @@ type SlackCommandResponse struct { Blocks []SlackBlock `json:"blocks"` } -func linkAddShort(c echo.Context, url, teamID, slackUser, text string) (*SlackCommandResponse, error) { +func linkAddShort(c echo.Context, surl, teamID, slackUser, text string) (*SlackCommandResponse, error) { opts := &database.FilterOptions{ Filter: sq.Eq{"sc.team_id": teamID}, Limit: 1, } + gctx := c.(*server.Context) ctx := c.Request().Context() lt := localizer.GetSessionLocalizer(c) slackConns, err := models.GetSlackConnections(ctx, opts) @@ -64,7 +68,7 @@ func linkAddShort(c echo.Context, url, teamID, slackUser, text string) (*SlackCo } if len(slackUsers) == 0 { - return sendInstructionLink(c, slackConn, url, slackUser) + return sendInstructionLink(c, slackConn, surl, slackUser) } user, err := models.GetUser(ctx, slackUsers[0].UserID, true) @@ -78,38 +82,66 @@ func linkAddShort(c echo.Context, url, teamID, slackUser, text string) (*SlackCo // index 0 is url // index 1 is shortcode // index 2 is domain - input := strings.Split(text, " ") - shortCodeArg := strings.Split(input[1], ":") - if len(shortCodeArg) != 2 { - block.Text.Text = lt.Translate("Wrong short code argument") + var lurl, domstr, code string + + for _, blob := range strings.Split(text, " ") { + _, err = url.Parse(blob) + if err != nil { + lurl = blob + continue + } + + parts := strings.SplitN(blob, ":", 2) + if len(parts) != 2 { + // Invalid input, skip + continue + } + + if !slices.Contains([]string{"code", "domain"}, parts[0]) { + // Invalid keyword, skip + continue + } + + if parts[0] == "code" { + code = parts[1] + } else { + domstr = parts[1] + } + } + + if lurl == "" { + block.Text.Text = lt.Translate("No URL argument was given") commandResp := &SlackCommandResponse{ Text: "Error", Blocks: []SlackBlock{block}, } return commandResp, nil - } - domainArg := strings.Split(input[2], ":") - if len(domainArg) != 2 { - block.Text.Text = lt.Translate("Wrong short code argument") + if domstr == "" { + block.Text.Text = lt.Translate("Wrong domain argument") commandResp := &SlackCommandResponse{ Text: "Error", Blocks: []SlackBlock{block}, } return commandResp, nil - } // Check that the domain exists, it is a short service domain // and belong to the slack connection organization opts = &database.FilterOptions{ Filter: sq.And{ - sq.Eq{"d.lookup_name": domainArg[1]}, + sq.Eq{"d.lookup_name": strings.ToLower(domstr)}, sq.Eq{"d.service": models.DomainServiceShort}, sq.Eq{"d.status": models.DomainStatusApproved}, sq.Eq{"d.is_active": true}, - sq.Eq{"d.org_id": slackConn.OrgID}, + sq.Or{ + sq.And{ + sq.Eq{"d.org_id": slackConn.OrgID}, + sq.Eq{"d.level": models.DomainLevelUser}, + }, + sq.Eq{"d.level": models.DomainLevelSystem}, + }, }, Limit: 1, } @@ -119,7 +151,7 @@ func linkAddShort(c echo.Context, url, teamID, slackUser, text string) (*SlackCo return nil, err } if len(domains) == 0 { - block.Text.Text = lt.Translate("%s: domain not found", domainArg[1]) + block.Text.Text = lt.Translate("%s: domain not found", domstr) commandResp := &SlackCommandResponse{ Text: "Error", Blocks: []SlackBlock{block}, @@ -143,21 +175,30 @@ func linkAddShort(c echo.Context, url, teamID, slackUser, text string) (*SlackCo domainId: $domain, shortCode: $short}) { id + shortCode } }`) - op.Var("title", input[0]) - op.Var("url", input[0]) + if len(lurl) > 150 { + op.Var("title", fmt.Sprintf("%s...", lurl[:146])) + } else { + op.Var("title", lurl) + } + op.Var("url", lurl) op.Var("slug", slackConn.OrgSlug) op.Var("domain", domain.ID) - op.Var("short", shortCodeArg[1]) + op.Var("short", code) err = links.Execute(ctx, op, &result) if err != nil { return nil, err } - block.Type = "section" - block.Text.Type = "mrkdwn" - block.Text.Text = lt.Translate("Your short link was successfully created") + + shortURL := &url.URL{ + Scheme: gctx.Server.Config.Scheme, + Host: domain.LookupName, + Path: "/" + result.LinkShort.ShortCode, + } + block.Text.Text = lt.Translate("Your short link was successfully created: %s", shortURL.String()) commandResp := &SlackCommandResponse{ Text: "Add Link", Blocks: []SlackBlock{block}, -- 2.45.2