Persist, renew and delete sessions, refactor storage package, move reusable packages to pkg

This commit is contained in:
Ken-Håvard Lieng 2018-05-31 23:24:59 +02:00
parent 121582f72a
commit 24f9553aa5
48 changed files with 1872 additions and 1171 deletions

106
pkg/session/session.go Normal file
View file

@ -0,0 +1,106 @@
package session
import (
"crypto/rand"
"encoding/base64"
"net/http"
"sync"
"time"
)
var (
CookieName = "session"
Expiration = time.Hour * 24 * 7
RefreshInterval = time.Hour
)
type Session struct {
UserID uint64
key string
createdAt int64
expiration *time.Timer
lock sync.Mutex
}
func New(id uint64) (*Session, error) {
key, err := newSessionKey()
if err != nil {
return nil, err
}
return &Session{
key: key,
createdAt: time.Now().Unix(),
UserID: id,
expiration: time.NewTimer(Expiration),
}, nil
}
func (s *Session) Init() {
exp := time.Until(time.Unix(s.createdAt, 0).Add(Expiration))
s.expiration = time.NewTimer(exp)
}
func (s *Session) Key() string {
s.lock.Lock()
key := s.key
s.lock.Unlock()
return key
}
func (s *Session) SetCookie(w http.ResponseWriter, r *http.Request) {
http.SetCookie(w, &http.Cookie{
Name: CookieName,
Value: s.Key(),
Path: "/",
Expires: time.Now().Add(Expiration),
HttpOnly: true,
Secure: r.TLS != nil,
})
}
func (s *Session) Expired() bool {
s.lock.Lock()
created := time.Unix(s.createdAt, 0)
s.lock.Unlock()
return time.Since(created) > Expiration
}
func (s *Session) Refresh() (string, bool, error) {
s.lock.Lock()
created := time.Unix(s.createdAt, 0)
s.lock.Unlock()
if time.Since(created) > Expiration {
return "", true, nil
}
if time.Since(created) > RefreshInterval {
key, err := newSessionKey()
if err != nil {
return "", false, err
}
s.expiration.Reset(Expiration)
s.lock.Lock()
s.createdAt = time.Now().Unix()
s.key = key
s.lock.Unlock()
return key, false, nil
}
return "", false, nil
}
func (s *Session) WaitUntilExpiration() {
<-s.expiration.C
}
func newSessionKey() (string, error) {
key := make([]byte, 32)
_, err := rand.Read(key)
return base64.RawURLEncoding.EncodeToString(key), err
}

View file

@ -0,0 +1,5 @@
struct Session {
UserID uint64
key string
createdAt int64
}

View file

@ -0,0 +1,112 @@
package session
import (
"io"
"time"
"unsafe"
)
var (
_ = unsafe.Sizeof(0)
_ = io.ReadFull
_ = time.Now()
)
func (d *Session) Size() (s uint64) {
{
l := uint64(len(d.key))
{
t := l
for t >= 0x80 {
t >>= 7
s++
}
s++
}
s += l
}
s += 16
return
}
func (d *Session) Marshal(buf []byte) ([]byte, error) {
size := d.Size()
{
if uint64(cap(buf)) >= size {
buf = buf[:size]
} else {
buf = make([]byte, size)
}
}
i := uint64(0)
{
*(*uint64)(unsafe.Pointer(&buf[0])) = d.UserID
}
{
l := uint64(len(d.key))
{
t := uint64(l)
for t >= 0x80 {
buf[i+8] = byte(t) | 0x80
t >>= 7
i++
}
buf[i+8] = byte(t)
i++
}
copy(buf[i+8:], d.key)
i += l
}
{
*(*int64)(unsafe.Pointer(&buf[i+8])) = d.createdAt
}
return buf[:i+16], nil
}
func (d *Session) Unmarshal(buf []byte) (uint64, error) {
i := uint64(0)
{
d.UserID = *(*uint64)(unsafe.Pointer(&buf[i+0]))
}
{
l := uint64(0)
{
bs := uint8(7)
t := uint64(buf[i+8] & 0x7F)
for buf[i+8]&0x80 == 0x80 {
i++
t |= uint64(buf[i+8]&0x7F) << bs
bs += 7
}
i++
l = t
}
d.key = string(buf[i+8 : i+8+l])
i += l
}
{
d.createdAt = *(*int64)(unsafe.Pointer(&buf[i+8]))
}
return i + 16, nil
}