view pkg/models/user.go @ 1697:f1c3fe8b79f5

Imports: Fixed some golint issues.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Mon, 07 Jan 2019 11:02:48 +0100
parents a244b18cb916
children 71b722809b2b
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>
//  * Tom Gottfried <tom.gottfried@intevation.de>

package models

import (
	"database/sql/driver"
	"encoding/json"
	"errors"
	"regexp"
	"strings"
)

type (
	// Email is a string formed by a valid EMail address.
	Email string
	// Country is a valid country 2 letter code.
	Country string
	// Role is a string with a valid gemma role.
	Role string
	// UserName is a string forming a valid user name.
	UserName string

	// BoundingBox is a spatial bounding box of user's
	// responsibility area.
	BoundingBox struct {
		X1 float64 `json:"x1"`
		Y1 float64 `json:"y1"`
		X2 float64 `json:"x2"`
		Y2 float64 `json:"y2"`
	}

	// User is a serialized JSON form of user data.
	User struct {
		User     UserName     `json:"user"`
		Role     Role         `json:"role"`
		Password string       `json:"password,omitempty"`
		Email    Email        `json:"email"`
		Country  Country      `json:"country"`
		Extent   *BoundingBox `json:"extent"`
	}

	// PWResetUser is send to request a password reset for a user.
	PWResetUser struct {
		User string `json:"user"`
	}
)

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

// UnmarshalJSON ensures that the given string forms 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
}

// Value implements the driver.Valuer interface.
func (e Email) Value() (driver.Value, error) {
	return string(e), nil
}

// Scan implements the sql.Scanner interface.
func (e *Email) Scan(src interface{}) (err error) {
	if s, ok := src.(string); ok {
		*e = Email(s)
	} else {
		err = errNoString
	}
	return
}

var errNoValidUser = errors.New("Not a valid user")

// IsValid checks if a given user name is valid.
func (u UserName) IsValid() bool {
	return u != ""
}

// UnmarshalJSON ensures that the given string forms a valid user name.
func (u *UserName) UnmarshalJSON(data []byte) error {
	var s string
	if err := json.Unmarshal(data, &s); err != nil {
		return err
	}
	if user := UserName(s); user.IsValid() {
		*u = user
		return nil
	}
	return errNoValidUser
}

// Scan implements the sql.Scanner interface.
func (u *UserName) Scan(src interface{}) (err error) {
	if s, ok := src.(string); ok {
		*u = UserName(s)
	} else {
		err = errNoString
	}
	return
}

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

// UnmarshalJSON ensures that the given string forms a valid
// two letter country code.
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
}

// Value implements the driver.Valuer interface.
func (c Country) Value() (driver.Value, error) {
	return string(c), nil
}

// Scan implements the sql.Scanner interfaces.
func (c *Country) Scan(src interface{}) (err error) {
	if s, ok := src.(string); ok {
		*c = Country(s)
	} else {
		err = errNoString
	}
	return
}

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

// Value implements the driver.Valuer interface.
func (r Role) Value() (driver.Value, error) {
	return string(r), nil
}

// Scan implements the sql.Scanner interface.
func (r *Role) Scan(src interface{}) (err error) {
	if s, ok := src.(string); ok {
		*r = Role(s)
	} else {
		err = errNoString
	}
	return
}

// UnmarshalJSON ensure that the given string is a valid user name.
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
}