user.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. /*
  2. * Public Domain Software
  3. *
  4. * I (Matthias Ladkau) am the author of the source code in this file.
  5. * I have placed the source code in this file in the public domain.
  6. *
  7. * For further information see: http://creativecommons.org/publicdomain/zero/1.0/
  8. */
  9. package user
  10. import (
  11. "crypto/rand"
  12. "errors"
  13. "fmt"
  14. "io"
  15. "net/http"
  16. "net/url"
  17. "sync"
  18. "devt.de/krotik/common/datautil"
  19. "devt.de/krotik/common/errorutil"
  20. )
  21. /*
  22. cookieName defines the session cookie name
  23. */
  24. const cookieNameSession = "~sid"
  25. /*
  26. CookieMaxLifetime is the max life time of a session cookie in seconds
  27. */
  28. var CookieMaxLifetime = 3600
  29. /*
  30. UserSessionManager manages all user sessions.
  31. */
  32. var UserSessionManager = &SessionManager{sync.Mutex{},
  33. NewMemorySessionProvider()}
  34. /*
  35. SessionManager manages web sessions.
  36. */
  37. type SessionManager struct {
  38. Lock sync.Mutex
  39. Provider SessionProvider
  40. }
  41. /*
  42. newSessionId creates a new session id.
  43. */
  44. func (manager *SessionManager) newSessionID() string {
  45. b := make([]byte, 32)
  46. _, err := io.ReadFull(rand.Reader, b)
  47. errorutil.AssertOk(err)
  48. return fmt.Sprintf("S-%x", b)
  49. }
  50. /*
  51. CheckSessionCookie checks if a request contains a session cookie and if the
  52. session is active. Returns has cookie and is active.
  53. */
  54. func (manager *SessionManager) CheckSessionCookie(r *http.Request) (bool, bool) {
  55. var session Session
  56. cookie, _ := r.Cookie(cookieNameSession)
  57. if cookie != nil {
  58. sid, _ := url.QueryUnescape(cookie.Value)
  59. session, _ = manager.Provider.Get(sid)
  60. }
  61. return cookie != nil, session != nil
  62. }
  63. /*
  64. RemoveSessionCookie removes the session cookie in a given response object.
  65. */
  66. func (manager *SessionManager) RemoveSessionCookie(w http.ResponseWriter) {
  67. cookie := http.Cookie{
  68. Name: cookieNameSession,
  69. Value: "",
  70. Path: "/",
  71. HttpOnly: true,
  72. MaxAge: -1,
  73. }
  74. http.SetCookie(w, &cookie)
  75. }
  76. /*
  77. GetSession retrieves an existing or creates a new session
  78. */
  79. func (manager *SessionManager) GetSession(user string, w http.ResponseWriter,
  80. r *http.Request, create bool) (Session, error) {
  81. manager.Lock.Lock()
  82. defer manager.Lock.Unlock()
  83. var session Session
  84. var err error
  85. var sid string
  86. // Retrieve the cookie
  87. cookie, cerr := r.Cookie(cookieNameSession)
  88. if cookie == nil || cookie.Value == "" {
  89. if !create {
  90. // Session is not present and it should not be created
  91. return nil, nil
  92. }
  93. // Session is not created if no user is present
  94. if user == "" {
  95. return nil, errors.New("Cannot create a session without a user")
  96. }
  97. // No cookie present - create a new session
  98. sid = manager.newSessionID()
  99. session, _ = manager.Provider.Init(sid, user)
  100. } else {
  101. // Session should be available
  102. sid, _ = url.QueryUnescape(cookie.Value)
  103. session, err = manager.Provider.Get(sid)
  104. }
  105. if create {
  106. // Write the session cookie in the response
  107. cookie = &http.Cookie{
  108. Name: cookieNameSession,
  109. Value: url.QueryEscape(sid),
  110. Path: "/",
  111. HttpOnly: true,
  112. MaxAge: CookieMaxLifetime,
  113. }
  114. http.SetCookie(w, cookie)
  115. }
  116. if cerr == http.ErrNoCookie {
  117. // Also register the cookie in the request so the session can
  118. // can be found by subsequent calls
  119. r.AddCookie(cookie)
  120. }
  121. return session, err
  122. }
  123. /*
  124. SessionProvider is a session storage provider. Sessions should expire
  125. after a certain amount of time.
  126. */
  127. type SessionProvider interface {
  128. /*
  129. Create a new session for a given user. The session has an explicit
  130. expiry time after which a get will fail.
  131. */
  132. Init(sid string, user string) (Session, error)
  133. /*
  134. Get retrieves a session.
  135. */
  136. Get(sid string) (Session, error)
  137. /*
  138. GetAll returns a list of all sessions.
  139. */
  140. GetAll() ([]Session, error)
  141. /*
  142. Destroy destroys a session.
  143. */
  144. Destroy(sid string) error
  145. }
  146. /*
  147. MemorySessionProvider keeps all session related data in memory.
  148. */
  149. type MemorySessionProvider struct {
  150. sessions *datautil.MapCache // Thread safe memory cache
  151. }
  152. /*
  153. NewMemorySessionProvider creates a new memory session provider. By default
  154. sessions have the same expiry time as cookies.
  155. */
  156. func NewMemorySessionProvider() SessionProvider {
  157. ret := &MemorySessionProvider{}
  158. ret.SetExpiry(CookieMaxLifetime)
  159. return ret
  160. }
  161. /*
  162. SetExpiry sets the session expiry time in seconds. All existing sessions
  163. are deleted during this function call. This call is not thread safe - only
  164. use it during initialisation!
  165. */
  166. func (ms *MemorySessionProvider) SetExpiry(secs int) {
  167. ms.sessions = datautil.NewMapCache(0, int64(secs))
  168. }
  169. /*
  170. Init creates a new session for a given user. The session has an explicit
  171. expiry time after which a get will fail.
  172. */
  173. func (ms *MemorySessionProvider) Init(sid string, user string) (Session, error) {
  174. session := NewDefaultSession(sid, user)
  175. ms.sessions.Put(sid, session)
  176. return session, nil
  177. }
  178. /*
  179. Get retrieves a session.
  180. */
  181. func (ms *MemorySessionProvider) Get(sid string) (Session, error) {
  182. if session, _ := ms.sessions.Get(sid); session != nil {
  183. return session.(Session), nil
  184. }
  185. return nil, nil
  186. }
  187. /*
  188. GetAll returns a list of all sessions.
  189. */
  190. func (ms *MemorySessionProvider) GetAll() ([]Session, error) {
  191. sessions := make([]Session, 0, ms.sessions.Size())
  192. for _, s := range ms.sessions.GetAll() {
  193. sessions = append(sessions, s.(Session))
  194. }
  195. return sessions, nil
  196. }
  197. /*
  198. Destroy destroys a session.
  199. */
  200. func (ms *MemorySessionProvider) Destroy(sid string) error {
  201. ms.sessions.Remove(sid)
  202. return nil
  203. }