view pkg/auth/session.go @ 904:e4b72a199258

New default bottleneck colors Mainly to make the stroke color one actually selectable in the ui. In addition the pink does better match the collors used on the ECDIS layer.
author Sascha Wilde <wilde@intevation.de>
date Tue, 02 Oct 2018 13:34:59 +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
}