view controllers/user.go @ 327:363983d5c567

Allow Waterway User to update a limited set of profile attributes
author Tom Gottfried <tom@intevation.de>
date Thu, 02 Aug 2018 19:25:30 +0200
parents a7b2db8b3d18
children 003243ec5ce5
line wrap: on
line source

package controllers

import (
	"database/sql"
	"fmt"
	"net/http"

	"github.com/gorilla/mux"

	"gemma.intevation.de/gemma/auth"
)

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)`

	updateUserUnprivSQL = `UPDATE users.list_users
  SET (pw, map_extent, email_address)
  = ($2, ST_MakeBox2D(ST_Point($3, $4), ST_Point($5, $6)), $7)
  WHERE username = $1`
	updateUserSQL = `UPDATE users.list_users
  SET (rolname, username, pw, country, map_extent, email_address)
  = ($2, $3, $4, $5, NULL, $6)
  WHERE username = $1`
	updateUserExtentSQL = `UPDATE users.list_users
  SET (rolname, username, pw, country, map_extent, email_address)
  = ($2, $3, $4, $5, ST_MakeBox2D(ST_Point($6, $7), ST_Point($8, $9)), $10)
  WHERE username = $1`

	deleteUserSQL = `SELECT sys_admin.delete_user($1)`

	listUsersSQL = `SELECT
  rolname,
  username,
  country,
  email_address,
  ST_XMin(map_extent), ST_YMin(map_extent),
  ST_XMax(map_extent), ST_YMax(map_extent)
FROM users.list_users`

	listUserSQL = `SELECT
  rolname,
  country,
  email_address,
  ST_XMin(map_extent), ST_YMin(map_extent),
  ST_XMax(map_extent), ST_YMax(map_extent)
FROM users.list_users
WHERE username = $1`
)

func deleteUser(
	_ interface{}, req *http.Request,
	db *sql.DB,
) (jr JSONResult, err error) {

	user := mux.Vars(req)["user"]
	if user == "" {
		err = JSONError{http.StatusBadRequest, "error: user empty"}
		return
	}

	session, _ := auth.GetSession(req)
	if session.User == user {
		err = JSONError{http.StatusBadRequest, "error: cannot delete yourself"}
		return
	}

	if _, err = db.Exec(deleteUserSQL, user); err != nil {
		return
	}

	// Running in a go routine should not be necessary.
	go func() { auth.ConnPool.Logout(user) }()

	jr = JSONResult{Code: http.StatusNoContent}
	return
}

func updateUser(
	input interface{}, req *http.Request,
	db *sql.DB,
) (jr JSONResult, err error) {

	user := mux.Vars(req)["user"]
	if user == "" {
		err = JSONError{http.StatusBadRequest, "error: user empty"}
		return
	}

	newUser := input.(*User)
	var res sql.Result

	if s, _ := auth.GetSession(req); s.Roles.Has("sys_admin") {
		if newUser.Extent == nil {
			res, err = db.Exec(
				updateUserSQL,
				user,
				newUser.Role,
				newUser.User,
				newUser.Password,
				newUser.Country,
				newUser.Email,
			)
		} else {
			res, err = db.Exec(
				updateUserExtentSQL,
				user,
				newUser.Role,
				newUser.User,
				newUser.Password,
				newUser.Country,
				newUser.Extent.X1, newUser.Extent.Y1,
				newUser.Extent.X2, newUser.Extent.Y2,
				newUser.Email,
			)
		}
	} else {
		res, err = db.Exec(
			updateUserUnprivSQL,
			user,
			newUser.Password,
			newUser.Extent.X1, newUser.Extent.Y1,
			newUser.Extent.X2, newUser.Extent.Y2,
			newUser.Email,
		)
	}

	if err != nil {
		return
	}

	if n, err2 := res.RowsAffected(); err2 == nil && n == 0 {
		err = JSONError{
			Code:    http.StatusNotFound,
			Message: fmt.Sprintf("Cannot find user %s.", user),
		}
		return
	}

	if user != newUser.User {
		// Running in a go routine should not be necessary.
		go func() { auth.ConnPool.Logout(user) }()
	}

	jr = JSONResult{
		Code: http.StatusCreated,
		Result: struct {
			Result string `json:"result"`
		}{"success"},
	}
	return
}

func createUser(
	input interface{}, req *http.Request,
	db *sql.DB,
) (jr JSONResult, err error) {

	user := input.(*User)

	if user.Extent == nil {
		_, err = db.Exec(
			createUserSQL,
			user.Role,
			user.User,
			user.Password,
			user.Country,
			user.Email,
		)
	} else {
		_, err = db.Exec(
			createUserExtentSQL,
			user.Role,
			user.User,
			user.Password,
			user.Country,
			user.Extent.X1, user.Extent.Y1,
			user.Extent.X2, user.Extent.Y2,
			user.Email,
		)
	}

	if err != nil {
		return
	}

	jr = JSONResult{
		Code: http.StatusCreated,
		Result: struct {
			Result string `json:"result"`
		}{"success"},
	}
	return
}

func listUsers(
	_ interface{}, req *http.Request,
	db *sql.DB,
) (jr JSONResult, err error) {

	var rows *sql.Rows

	rows, err = db.Query(listUsersSQL)
	if err != nil {
		return
	}
	defer rows.Close()

	var users []*User

	for rows.Next() {
		user := &User{Extent: &BoundingBox{}}
		if err = rows.Scan(
			&user.Role,
			&user.User,
			&user.Country,
			&user.Email,
			&user.Extent.X1, &user.Extent.Y1,
			&user.Extent.X2, &user.Extent.Y2,
		); err != nil {
			return
		}
		users = append(users, user)
	}

	jr = JSONResult{
		Result: struct {
			Users []*User `json:"users"`
		}{users},
	}
	return
}

func listUser(
	_ interface{}, req *http.Request,
	db *sql.DB,
) (jr JSONResult, err error) {

	user := mux.Vars(req)["user"]
	if user == "" {
		err = JSONError{http.StatusBadRequest, "error: user empty"}
		return
	}

	result := &User{
		User:   user,
		Extent: &BoundingBox{},
	}

	err = db.QueryRow(listUserSQL, user).Scan(
		&result.Role,
		&result.Country,
		&result.Email,
		&result.Extent.X1, &result.Extent.Y1,
		&result.Extent.X2, &result.Extent.Y2,
	)

	switch {
	case err == sql.ErrNoRows:
		err = JSONError{
			Code:    http.StatusNotFound,
			Message: fmt.Sprintf("Cannot find user %s.", user),
		}
		return
	case err != nil:
		return
	}

	jr.Result = result
	return
}