view pkg/controllers/printtemplates.go @ 2267:37ae1bee3e4a

Ajjusted RLS for user templates.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Thu, 14 Feb 2019 15:11:00 +0100
parents e6fba449aa3c
children 1c8b8a4476af
line wrap: on
line source

// This is Free Software under GNU Affero General Public License v >= 3.0
// without warranty, see README.md and license for details.
//
// SPDX-License-Identifier: AGPL-3.0-or-later
// License-Filename: LICENSES/AGPL-3.0.txt
//
// Copyright (C) 2018 by via donau
//   – Österreichische Wasserstraßen-Gesellschaft mbH
// Software engineering by Intevation GmbH
//
// Author(s):
//  * Sascha L. Teichmann <sascha.teichmann@intevation.de>

package controllers

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"net/http"
	"time"

	"github.com/gorilla/mux"
	"github.com/jackc/pgx/pgtype"

	"gemma.intevation.de/gemma/pkg/models"
)

const maxPrintTemplateSize = 5 * 1024 * 1024

const (
	listPrintTemplatesSQL = `
SELECT
  template_name,
  date_info
FROM
  users.templates
ORDER BY date_info DESC`

	hasPrintTemplateSQL = `
SELECT true FROM users.templates WHERE template_name = $1`

	deletePrintTemplateSQL = `
DELETE FROM users.templates WHERE template_name = $1`

	selectPrintTemplateSQL = `
SELECT template_data FROM users.templates WHERE template_name = $1`

	insertPrintTemplateSQL = `
INSERT INTO users.templates (template_name, template_data, country)
SELECT
  $1,
  $2,
  CASE WHEN pg_has_role('sys_admin', 'MEMBER') THEN NULL
       ELSE users.current_user_country()
  END`

	updatePrintTemplateSQL = `
UPDATE user.templates template_data = $2 WHERE template_name = $1`
)

func listPrintTemplates(
	_ interface{},
	req *http.Request,
	conn *sql.Conn,
) (jr JSONResult, err error) {

	type template struct {
		Name string      `json:"name"`
		Time models.Time `json:"time"`
	}

	var rows *sql.Rows
	if rows, err = conn.QueryContext(req.Context(), listPrintTemplatesSQL); err != nil {
		return
	}
	defer rows.Close()

	templates := []*template{}

	for rows.Next() {
		var tmpl template
		var w time.Time
		if err = rows.Scan(&tmpl.Name, &w); err != nil {
			return
		}
		tmpl.Time = models.Time{w}
		templates = append(templates, &tmpl)
	}

	jr = JSONResult{Result: templates}
	return
}

func fetchPrintTemplate(
	_ interface{},
	req *http.Request,
	conn *sql.Conn,
) (jr JSONResult, err error) {

	ctx := req.Context()
	name := mux.Vars(req)["name"]

	var data pgtype.Bytea
	err = conn.QueryRowContext(ctx, selectPrintTemplateSQL, name).Scan(&data)

	switch {
	case err == sql.ErrNoRows:
		err = JSONError{
			Code:    http.StatusNotFound,
			Message: "No such template found",
		}
		return
	case err != nil:
		return
	case data.Status != pgtype.Present:
		err = JSONError{
			Code:    http.StatusInternalServerError,
			Message: "Unexpected return value from database query",
		}
		return
	}
	jr = JSONResult{Result: bytes.NewReader(data.Bytes)}
	return
}

func createPrintTemplate(
	input interface{},
	req *http.Request,
	conn *sql.Conn,
) (jr JSONResult, err error) {

	ctx := req.Context()
	name := mux.Vars(req)["name"]
	in := input.(*json.RawMessage)

	if name == "" {
		err = JSONError{
			Code:    http.StatusBadRequest,
			Message: "Template must have a none empty name",
		}
		return
	}
	if len(*in) == 0 {
		err = JSONError{
			Code:    http.StatusBadRequest,
			Message: "Template must have a none empty template",
		}
		return
	}
	var tx *sql.Tx
	if tx, err = conn.BeginTx(ctx, nil); err != nil {
		return
	}
	defer tx.Rollback()

	var dummy bool
	err = tx.QueryRowContext(ctx, hasPrintTemplateSQL, name).Scan(&dummy)

	switch {
	case err == sql.ErrNoRows:
		// This is fine.
	case err != nil:
		return
	default:
		err = JSONError{
			Code:    http.StatusBadRequest,
			Message: "A template with this name already exists",
		}
		return
	}
	data := pgtype.Bytea{Bytes: *in, Status: pgtype.Present}

	if _, err = tx.ExecContext(ctx, insertPrintTemplateSQL, name, &data); err != nil {
		return
	}

	if err = tx.Commit(); err != nil {
		return
	}
	jr = JSONResult{
		Code: http.StatusCreated,
		Result: map[string]string{
			"created": name,
		},
	}
	return
}

func deletePrintTemplate(
	_ interface{},
	req *http.Request,
	conn *sql.Conn,
) (jr JSONResult, err error) {

	ctx := req.Context()
	name := mux.Vars(req)["name"]

	var tx *sql.Tx
	if tx, err = conn.BeginTx(ctx, nil); err != nil {
		return
	}
	defer tx.Rollback()

	var dummy bool
	err = tx.QueryRowContext(ctx, hasPrintTemplateSQL, name).Scan(&dummy)

	switch {
	case err == sql.ErrNoRows:
		err = JSONError{
			Code:    http.StatusNotFound,
			Message: "No such template found",
		}
		return
	case err != nil:
		return
	case !dummy:
		err = JSONError{
			Code:    http.StatusInternalServerError,
			Message: "Unexpected return value from database query",
		}
		return
	}

	if _, err = tx.ExecContext(ctx, deletePrintTemplateSQL, name); err != nil {
		return
	}

	if err = tx.Commit(); err != nil {
		return
	}

	jr = JSONResult{
		Result: map[string]string{
			"deleted": name,
		},
	}

	return
}

func updatePrintTemplate(
	input interface{},
	req *http.Request,
	conn *sql.Conn,
) (jr JSONResult, err error) {

	ctx := req.Context()
	name := mux.Vars(req)["name"]
	in := input.(*json.RawMessage)

	if name == "" {
		err = JSONError{
			Code:    http.StatusBadRequest,
			Message: "Template must have a none empty name",
		}
		return
	}
	if len(*in) == 0 {
		err = JSONError{
			Code:    http.StatusBadRequest,
			Message: "Template must have a none empty template",
		}
		return
	}
	var tx *sql.Tx
	if tx, err = conn.BeginTx(ctx, nil); err != nil {
		return
	}
	defer tx.Rollback()

	var dummy bool
	err = tx.QueryRowContext(ctx, hasPrintTemplateSQL, name).Scan(&dummy)

	switch {
	case err == sql.ErrNoRows:
		err = JSONError{
			Code:    http.StatusNotFound,
			Message: "No such template found",
		}
		return
	case err != nil:
		return
	case !dummy:
		err = JSONError{
			Code:    http.StatusInternalServerError,
			Message: "Unexpected return value from database query",
		}
		return
	}
	data := pgtype.Bytea{Bytes: *in, Status: pgtype.Present}

	if _, err = tx.ExecContext(ctx, updatePrintTemplateSQL, name, &data); err != nil {
		return
	}

	if err = tx.Commit(); err != nil {
		return
	}

	jr = JSONResult{
		Code: http.StatusOK,
		Result: map[string]string{
			"updated": name,
		},
	}
	return
}