view controllers/user.go @ 226:63dd5216eee4

Refactored gemma server to be more REST-like.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Thu, 26 Jul 2018 12:24:30 +0200
parents cmd/gemma/user.go@bcd9016dcd4d
children 7d1f0ffdfa41
line wrap: on
line source

package controllers

import (
	"database/sql"
	"encoding/json"
	"errors"
	"log"
	"net/http"
	"regexp"
	"strings"

	"gemma.intevation.de/gemma/auth"
	"github.com/jackc/pgx"
)

type (
	Email   string
	Country string
	Role    string

	BoundingBox struct {
		X1 float64 `json:"x1"`
		Y1 float64 `json:"y1"`
		X2 float64 `json:"x2"`
		Y2 float64 `json:"y2"`
	}

	User struct {
		User     string       `json:"user"`
		Role     Role         `json:"role"`
		Password string       `json:"password"`
		Email    Email        `json:"email"`
		Country  Country      `json:"country"`
		Extent   *BoundingBox `json:"extent"`
	}
)

const (
	createUserSQL       = `SELECT sys_admin.create_user($1, $2, $3, $4, NULL, $5)`
	createUserExtentSQL = `SELECT sys_admin.create_user($1, $2, $3, $4,
  ST_MakeBox2D(ST_Point($5, $6), ST_Point($7, $8)), $9)`
)

var (
	// https://stackoverflow.com/questions/201323/how-to-validate-an-email-address-using-a-regular-expression
	emailRe = regexp.MustCompile(
		`(?:[a-z0-9!#$%&'*+/=?^_` + "`" +
			`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_` + "`" +
			`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]` +
			`|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")` +
			`@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?` +
			`|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}` +
			`(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]` +
			`:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]` +
			`|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])`)
	errNoEmailAddress = errors.New("Not a valid email address")
)

func (e *Email) UnmarshalJSON(data []byte) error {
	var s string
	if err := json.Unmarshal(data, &s); err != nil {
		return err
	}
	if !emailRe.MatchString(s) {
		return errNoEmailAddress
	}
	*e = Email(s)
	return nil
}

var (
	validCountries = []string{
		"AT", "BG", "DE", "HU", "HR",
		"MD", "RO", "RS", "SK", "UA",
	}
	errNoValidCountry = errors.New("Not a valid country")
)

func (c *Country) UnmarshalJSON(data []byte) error {
	var s string
	if err := json.Unmarshal(data, &s); err != nil {
		return err
	}
	s = strings.ToUpper(s)
	for _, v := range validCountries {
		if v == s {
			*c = Country(v)
			return nil
		}
	}
	return errNoValidCountry
}

var (
	validRoles = []string{
		"waterway_user",
		"waterway_admin",
		"sys_admin",
	}
	errNoValidRole = errors.New("Not a valid role")
)

func (r *Role) UnmarshalJSON(data []byte) error {
	var s string
	if err := json.Unmarshal(data, &s); err != nil {
		return err
	}
	s = strings.ToLower(s)
	for _, v := range validRoles {
		if v == s {
			*r = Role(v)
			return nil
		}
	}
	return errNoValidRole
}

func createUser(rw http.ResponseWriter, req *http.Request) {

	var user User

	defer req.Body.Close()
	if err := json.NewDecoder(req.Body).Decode(&user); err != nil {
		http.Error(rw, "error: "+err.Error(), http.StatusBadRequest)
		return
	}

	token, _ := auth.GetToken(req)
	err := auth.ConnPool.Do(token, func(db *sql.DB) (err error) {
		if user.Extent == nil {
			_, err = db.Exec(
				createUserSQL,
				string(user.Role),
				user.User,
				user.Password,
				string(user.Country),
				string(user.Email),
			)
		} else {
			_, err = db.Exec(
				createUserExtentSQL,
				string(user.Role),
				user.User,
				user.Password,
				string(user.Country),
				user.Extent.X1, user.Extent.Y1,
				user.Extent.X2, user.Extent.Y2,
				string(user.Email),
			)
		}
		return
	})

	var res struct {
		Result  string `json:"result"`
		Code    string `json:"code,omitempty"`
		Message string `json:"message,omitempty"`
	}

	if err != nil {
		if pgErr, ok := err.(pgx.PgError); ok {
			res.Result = "failure"
			res.Code = pgErr.Code
			res.Message = pgErr.Message
		} else {
			log.Printf("err: %v\n", err)
			http.Error(rw,
				"error: "+err.Error(),
				http.StatusInternalServerError)
			return
		}
	} else {
		res.Result = "success"
	}

	rw.Header().Set("Content-Type", "application/json")
	if err := json.NewEncoder(rw).Encode(&res); err != nil {
		log.Printf("error: %v\n", err)
	}
}