You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

147 lines
2.9 KiB

// Copyright 2018 Lars Hoogestraat
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package session
import (
"crypto/rand"
"encoding/base64"
"io"
"net/http"
"time"
)
//SessionService contains settings for the session
type SessionService struct {
Path string
HTTPOnly bool
Name string
Secure bool
IdleSessionTTL int64
SessionProvider SessionProvider
}
//Create creates the session for the request
func (sc SessionService) Create(rw http.ResponseWriter, r *http.Request) *Session {
sid := base64.StdEncoding.EncodeToString(randomSecureKey(64))
s := sc.SessionProvider.Create(sid)
cookie := &http.Cookie{
Path: sc.Path,
HttpOnly: sc.HTTPOnly,
Name: sc.Name,
Secure: sc.Secure,
Value: s.SessionID(),
}
http.SetCookie(rw, cookie)
return s
}
//Get receives the session from the cookie
func (sc SessionService) Get(rw http.ResponseWriter, r *http.Request) (*Session, error) {
cookie, err := r.Cookie(sc.Name)
if err != nil {
return nil, err
}
sess, err := sc.SessionProvider.Get(cookie.Value)
if err != nil {
//Try to remove client cookie as it is not valid anymore
dc := &http.Cookie{
Name: sc.Name,
MaxAge: -1,
Expires: time.Unix(1, 0),
Path: sc.Path,
}
http.SetCookie(rw, dc)
return nil, err
}
return sess, nil
}
//Renew renews the session
func (sc SessionService) Renew(rw http.ResponseWriter, r *http.Request) (*Session, error) {
cookie, err := r.Cookie(sc.Name)
if err != nil {
return nil, err
}
_, err = sc.SessionProvider.Get(cookie.Value)
if err != nil {
return nil, err
}
sc.SessionProvider.Remove(cookie.Value)
dc := &http.Cookie{
Name: sc.Name,
MaxAge: -1,
Expires: time.Unix(1, 0),
Path: sc.Path,
}
http.SetCookie(rw, dc)
sid := base64.StdEncoding.EncodeToString(randomSecureKey(64))
s := sc.SessionProvider.Create(sid)
cookie = &http.Cookie{
Path: sc.Path,
HttpOnly: sc.HTTPOnly,
Name: sc.Name,
Secure: sc.Secure,
Value: s.SessionID(),
}
http.SetCookie(rw, cookie)
return s, nil
}
//Remove removes the session from the session map and the cookie
func (sc SessionService) Remove(rw http.ResponseWriter, r *http.Request) error {
cookie, err := r.Cookie(sc.Name)
if err != nil {
return err
}
sc.SessionProvider.Remove(cookie.Value)
dc := &http.Cookie{
Name: sc.Name,
MaxAge: -1,
Expires: time.Unix(1, 0),
Path: sc.Path,
}
http.SetCookie(rw, dc)
return nil
}
//InitGC initialized the garbage collection for removing the session after the TTL has reached
func (sc SessionService) InitGC(ticker *time.Ticker, timeoutAfter time.Duration) {
sc.SessionProvider.Clean(ticker, timeoutAfter)
}
func randomSecureKey(length int) []byte {
k := make([]byte, length)
if _, err := io.ReadFull(rand.Reader, k); err != nil {
return nil
}
return k
}