From 7cae658211d7ae9bbfacf176f5d45ee3321a4b57 Mon Sep 17 00:00:00 2001
From: jensilo <k@jensheise.com>
Date: Fri, 15 Dec 2023 15:49:16 +0100
Subject: [PATCH] fix visibility bug with templates and template sets

---
 src/app/eiffel/service.go    | 10 ++++++++++
 src/app/eiffel/web.go        |  3 ++-
 src/app/template/template.go | 33 +++++++++++++++++++--------------
 3 files changed, 31 insertions(+), 15 deletions(-)

diff --git a/src/app/eiffel/service.go b/src/app/eiffel/service.go
index f739706..0164c37 100644
--- a/src/app/eiffel/service.go
+++ b/src/app/eiffel/service.go
@@ -55,6 +55,7 @@ func TemplateIntoBasicTemplate(t *template.Template, validator validation.V, rul
 // TemplateFormData struct. If the template or variant could not be found, an error is returned.
 // However, using the defaultFirstVariant flag, the first variant will be used if no variant was specified and no
 // error will be returned. TemplateFormFromRequest will also parse and validate the template.
+// TemplateFormFromRequest will return an error if the user is not permitted to access the template.
 //
 // Returned errors from TemplateFormFromRequest are safe to display to the user.
 func TemplateFormFromRequest(
@@ -76,6 +77,15 @@ func TemplateFormFromRequest(
 		return TemplateFormData{}, ErrTemplateNotFound
 	}
 
+	usr, err := user.CtxUser(ctx)
+	if err != nil {
+		return TemplateFormData{}, ErrTemplateNotFound
+	}
+
+	if tmpl.CreatedBy != usr.ID {
+		return TemplateFormData{}, ErrTemplateNotFound
+	}
+
 	bt, err := TemplateIntoBasicTemplate(tmpl, validator, ruleParsers)
 	if err != nil {
 		return TemplateFormData{}, err
diff --git a/src/app/eiffel/web.go b/src/app/eiffel/web.go
index fba9462..e3e2a72 100644
--- a/src/app/eiffel/web.go
+++ b/src/app/eiffel/web.go
@@ -202,7 +202,8 @@ func searchTemplate(appCtx *hctx.AppCtx, webCtx *web.Ctx) http.Handler {
 			)
 		}
 
-		templates, err := templateRepository.FindByQueryForType(io.Context(), query, BasicTemplateType)
+		ctx := io.Context()
+		templates, err := templateRepository.FindByQueryForTypeAndUser(ctx, query, BasicTemplateType, user.MustCtxUser(ctx))
 		if err != nil && !errors.Is(err, persistence.ErrNotFound) {
 			return io.InlineError(web.ErrInternal, err)
 		}
diff --git a/src/app/template/template.go b/src/app/template/template.go
index 8ea099b..4104720 100644
--- a/src/app/template/template.go
+++ b/src/app/template/template.go
@@ -6,6 +6,7 @@ import (
 	"errors"
 	"github.com/google/uuid"
 	"github.com/jackc/pgx/v5/pgxpool"
+	"github.com/org-harmony/harmony/src/app/user"
 	"github.com/org-harmony/harmony/src/core/persistence"
 	"strings"
 	"time"
@@ -31,15 +32,17 @@ var (
 // Each template belongs to a template set. Templates are versioned and the information about the template should always match the template's config JSON.
 // Actually, Type, Name and Version are redundant, but they are used for easier querying.
 type Template struct {
-	ID              uuid.UUID
-	TemplateSet     uuid.UUID
-	Type            string
-	Name            string
-	Version         string
-	Config          string
-	CreatedBy       uuid.UUID
-	CreatedAt       time.Time
-	UpdatedAt       *time.Time
+	ID          uuid.UUID
+	TemplateSet uuid.UUID
+	Type        string
+	Name        string
+	Version     string
+	Config      string
+	CreatedBy   uuid.UUID
+	CreatedAt   time.Time
+	UpdatedAt   *time.Time
+	// TemplateSetElem is the template set that the template belongs to joined onto the template.
+	// Don't expect this to be filled unless the origin of the template object explicitly states that it is filled.
 	TemplateSetElem *Set
 }
 
@@ -110,11 +113,12 @@ type PGSetRepository struct {
 type Repository interface {
 	persistence.Repository
 
-	// FindByQueryForType finds all templates by a query for a specified template type.
+	// FindByQueryForTypeAndUser finds all templates by a query for a specified template type and user.
 	// The query will be searched for in the template's name, version and in the template set's name.
 	// It will join the template.Set onto template.Template and read it into Set.TemplateSetElem.
+	// The search is limited to the user's templates as templates are private.
 	// It returns persistence.ErrNotFound if no templates could be found and persistence.ErrReadRow for any other error.
-	FindByQueryForType(ctx context.Context, query string, templateType string) ([]*Template, error)
+	FindByQueryForTypeAndUser(ctx context.Context, query, templateType string, usr *user.User) ([]*Template, error)
 	// FindByID finds a template by its id.
 	// It returns persistence.ErrNotFound if the template could not be found and persistence.ErrReadRow for any other error.
 	FindByID(ctx context.Context, id uuid.UUID) (*Template, error)
@@ -227,18 +231,19 @@ func (r *PGSetRepository) RepositoryName() string {
 	return SetRepositoryName
 }
 
-// FindByQueryForType finds all templates by a query for a specified template type.
+// FindByQueryForTypeAndUser finds all templates by a query for a specified template type and user.
 // It returns persistence.ErrNotFound if no templates could be found and persistence.ErrReadRow for any other error.
-func (r *PGRepository) FindByQueryForType(ctx context.Context, query string, templateType string) ([]*Template, error) {
+func (r *PGRepository) FindByQueryForTypeAndUser(ctx context.Context, query, templateType string, usr *user.User) ([]*Template, error) {
 	rows, err := r.db.Query(
 		ctx,
 		`SELECT 
 templates.id, templates.template_set, templates.type, templates.name, templates.version, templates.config, templates.created_by, templates.created_at, templates.updated_at,
 template_sets.name, template_sets.version, template_sets.description, template_sets.created_by, template_sets.created_at, template_sets.updated_at
 FROM templates LEFT JOIN template_sets ON templates.template_set = template_sets.id
-WHERE templates.name ILIKE $1 OR templates.version ILIKE $1 OR template_sets.name ILIKE $1 AND templates.type = $2`,
+WHERE (templates.name ILIKE $1 OR templates.version ILIKE $1 OR template_sets.name ILIKE $1) AND templates.type = $2 AND templates.created_by = $3`,
 		"%"+query+"%",
 		templateType,
+		usr.ID,
 	)
 	if err != nil {
 		return nil, persistence.PGReadErr(err)
-- 
GitLab