view pkg/auth/session.go @ 942:912d016275ee

client: add arrow to drawn linesegment * Add styling function that will place an icon png image at the end of each drawn line segment, in the right rotation. Note that this does not look perfectly centered, see comment in the code.
author Bernhard Reiter <bernhard@intevation.de>
date Tue, 09 Oct 2018 18:39:01 +0200
parents e1466e65bc35
children a244b18cb916
line wrap: on
line source

package auth

import (
	"encoding/base64"
	"errors"
	"io"
	"sync"
	"time"

	"gemma.intevation.de/gemma/pkg/common"
	"gemma.intevation.de/gemma/pkg/misc"
)

type Roles []string

type Session struct {
	ExpiresAt int64  `json:"expires"`
	User      string `json:"user"`
	Roles     Roles  `json:"roles"`

	// private fields for managing expiration.
	access time.Time
	mu     sync.Mutex
}

func (r Roles) Has(role string) bool {
	for _, x := range r {
		if x == role {
			return true
		}
	}
	return false
}

func (r Roles) HasAny(roles ...string) bool {
	for _, y := range roles {
		if r.Has(y) {
			return true
		}
	}
	return false
}

const (
	sessionKeyLength = 20
	maxTokenValid    = time.Hour * 3
)

func NewSession(user, password string, roles Roles) *Session {

	// Create the Claims
	return &Session{
		ExpiresAt: time.Now().Add(maxTokenValid).Unix(),
		User:      user,
		Roles:     roles,
	}
}

func (s *Session) serialize(w io.Writer) error {

	access, err := s.last().MarshalText()
	if err != nil {
		return err
	}

	wr := misc.BinWriter{w, nil}
	wr.WriteBin(s.ExpiresAt)
	wr.WriteString(s.User)
	wr.WriteBin(uint32(len(s.Roles)))
	for _, role := range s.Roles {
		wr.WriteString(role)
	}
	wr.WriteBin(uint32(len(access)))
	wr.WriteBin(access)
	return wr.Err
}

func (s *Session) deserialize(r io.Reader) error {

	var session Session

	var n uint32
	rd := misc.BinReader{r, nil}
	rd.ReadBin(&session.ExpiresAt)
	rd.ReadString(&session.User)
	rd.ReadBin(&n)
	session.Roles = make(Roles, n)

	for i := uint32(0); n > 0 && i < n; i++ {
		rd.ReadString(&session.Roles[i])
	}

	if rd.Err != nil {
		return rd.Err
	}

	var l uint32
	rd.ReadBin(&l)
	access := make([]byte, l)
	rd.ReadBin(access)

	if rd.Err != nil {
		return rd.Err
	}

	var t time.Time
	if err := t.UnmarshalText(access); err != nil {
		return err
	}

	session.access = t

	*s = session

	return nil
}

func (c *Session) touch() {
	c.mu.Lock()
	c.access = time.Now()
	c.mu.Unlock()
}

func (c *Session) last() time.Time {
	c.mu.Lock()
	access := c.access
	c.mu.Unlock()
	return access
}

func GenerateSessionKey() string {
	return base64.URLEncoding.EncodeToString(
		common.GenerateRandomKey(sessionKeyLength))
}

var ErrInvalidRole = errors.New("Invalid role")

func GenerateSession(user, password string) (string, *Session, error) {
	roles, err := AllOtherRoles(user, password)
	if err != nil {
		return "", nil, err
	}
	if !roles.HasAny("sys_admin", "waterway_admin", "waterway_user") {
		return "", nil, ErrInvalidRole
	}
	token := GenerateSessionKey()
	session := NewSession(user, password, roles)
	Sessions.Add(token, session)
	return token, session, nil
}