123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- /*
- * Public Domain Software
- *
- * I (Matthias Ladkau) am the author of the source code in this file.
- * I have placed the source code in this file in the public domain.
- *
- * For further information see: http://creativecommons.org/publicdomain/zero/1.0/
- */
- package user
- import (
- "crypto/rand"
- "errors"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "sync"
- "devt.de/krotik/common/datautil"
- "devt.de/krotik/common/errorutil"
- )
- /*
- cookieName defines the session cookie name
- */
- const cookieNameSession = "~sid"
- /*
- CookieMaxLifetime is the max life time of a session cookie in seconds
- */
- var CookieMaxLifetime = 3600
- /*
- UserSessionManager manages all user sessions.
- */
- var UserSessionManager = &SessionManager{sync.Mutex{},
- NewMemorySessionProvider()}
- /*
- SessionManager manages web sessions.
- */
- type SessionManager struct {
- Lock sync.Mutex
- Provider SessionProvider
- }
- /*
- newSessionId creates a new session id.
- */
- func (manager *SessionManager) newSessionID() string {
- b := make([]byte, 32)
- _, err := io.ReadFull(rand.Reader, b)
- errorutil.AssertOk(err)
- return fmt.Sprintf("S-%x", b)
- }
- /*
- CheckSessionCookie checks if a request contains a session cookie and if the
- session is active. Returns has cookie and is active.
- */
- func (manager *SessionManager) CheckSessionCookie(r *http.Request) (bool, bool) {
- var session Session
- cookie, _ := r.Cookie(cookieNameSession)
- if cookie != nil {
- sid, _ := url.QueryUnescape(cookie.Value)
- session, _ = manager.Provider.Get(sid)
- }
- return cookie != nil, session != nil
- }
- /*
- RemoveSessionCookie removes the session cookie in a given response object.
- */
- func (manager *SessionManager) RemoveSessionCookie(w http.ResponseWriter) {
- cookie := http.Cookie{
- Name: cookieNameSession,
- Value: "",
- Path: "/",
- HttpOnly: true,
- MaxAge: -1,
- }
- http.SetCookie(w, &cookie)
- }
- /*
- GetSession retrieves an existing or creates a new session
- */
- func (manager *SessionManager) GetSession(user string, w http.ResponseWriter,
- r *http.Request, create bool) (Session, error) {
- manager.Lock.Lock()
- defer manager.Lock.Unlock()
- var session Session
- var err error
- var sid string
- // Retrieve the cookie
- cookie, cerr := r.Cookie(cookieNameSession)
- if cookie == nil || cookie.Value == "" {
- if !create {
- // Session is not present and it should not be created
- return nil, nil
- }
- // Session is not created if no user is present
- if user == "" {
- return nil, errors.New("Cannot create a session without a user")
- }
- // No cookie present - create a new session
- sid = manager.newSessionID()
- session, _ = manager.Provider.Init(sid, user)
- } else {
- // Session should be available
- sid, _ = url.QueryUnescape(cookie.Value)
- session, err = manager.Provider.Get(sid)
- }
- if create {
- // Write the session cookie in the response
- cookie = &http.Cookie{
- Name: cookieNameSession,
- Value: url.QueryEscape(sid),
- Path: "/",
- HttpOnly: true,
- MaxAge: CookieMaxLifetime,
- }
- http.SetCookie(w, cookie)
- }
- if cerr == http.ErrNoCookie {
- // Also register the cookie in the request so the session can
- // can be found by subsequent calls
- r.AddCookie(cookie)
- }
- return session, err
- }
- /*
- SessionProvider is a session storage provider. Sessions should expire
- after a certain amount of time.
- */
- type SessionProvider interface {
- /*
- Create a new session for a given user. The session has an explicit
- expiry time after which a get will fail.
- */
- Init(sid string, user string) (Session, error)
- /*
- Get retrieves a session.
- */
- Get(sid string) (Session, error)
- /*
- GetAll returns a list of all sessions.
- */
- GetAll() ([]Session, error)
- /*
- Destroy destroys a session.
- */
- Destroy(sid string) error
- }
- /*
- MemorySessionProvider keeps all session related data in memory.
- */
- type MemorySessionProvider struct {
- sessions *datautil.MapCache // Thread safe memory cache
- }
- /*
- NewMemorySessionProvider creates a new memory session provider. By default
- sessions have the same expiry time as cookies.
- */
- func NewMemorySessionProvider() SessionProvider {
- ret := &MemorySessionProvider{}
- ret.SetExpiry(CookieMaxLifetime)
- return ret
- }
- /*
- SetExpiry sets the session expiry time in seconds. All existing sessions
- are deleted during this function call. This call is not thread safe - only
- use it during initialisation!
- */
- func (ms *MemorySessionProvider) SetExpiry(secs int) {
- ms.sessions = datautil.NewMapCache(0, int64(secs))
- }
- /*
- Init creates a new session for a given user. The session has an explicit
- expiry time after which a get will fail.
- */
- func (ms *MemorySessionProvider) Init(sid string, user string) (Session, error) {
- session := NewDefaultSession(sid, user)
- ms.sessions.Put(sid, session)
- return session, nil
- }
- /*
- Get retrieves a session.
- */
- func (ms *MemorySessionProvider) Get(sid string) (Session, error) {
- if session, _ := ms.sessions.Get(sid); session != nil {
- return session.(Session), nil
- }
- return nil, nil
- }
- /*
- GetAll returns a list of all sessions.
- */
- func (ms *MemorySessionProvider) GetAll() ([]Session, error) {
- sessions := make([]Session, 0, ms.sessions.Size())
- for _, s := range ms.sessions.GetAll() {
- sessions = append(sessions, s.(Session))
- }
- return sessions, nil
- }
- /*
- Destroy destroys a session.
- */
- func (ms *MemorySessionProvider) Destroy(sid string) error {
- ms.sessions.Remove(sid)
- return nil
- }
|