auth.go 3.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. package main
  2. import (
  3. "bytes"
  4. "container/heap"
  5. "crypto/rand"
  6. "crypto/sha1"
  7. "encoding/base64"
  8. "errors"
  9. "golang.org/x/crypto/pbkdf2"
  10. "log"
  11. "net/http"
  12. "strings"
  13. "time"
  14. )
  15. type SessionData struct {
  16. sid string
  17. username string
  18. spotify string
  19. priority time.Time
  20. index int
  21. }
  22. type SessionQueue []*SessionData
  23. func (pq SessionQueue) Len() int {
  24. return len(pq)
  25. }
  26. func (pq SessionQueue) Less(i, j int) bool {
  27. return pq[i].priority.Before(pq[j].priority)
  28. }
  29. func (pq SessionQueue) Swap(i, j int) {
  30. pq[i], pq[j] = pq[j], pq[i]
  31. pq[i].index = i
  32. pq[j].index = j
  33. }
  34. func (pq *SessionQueue) Push(x interface{}) {
  35. n := len(*pq)
  36. item := x.(*SessionData)
  37. item.index = n
  38. *pq = append(*pq, item)
  39. }
  40. func (pq *SessionQueue) Pop() interface{} {
  41. old := *pq
  42. n := len(old)
  43. item := old[n-1]
  44. item.index = -1 // for safety
  45. *pq = old[0 : n-1]
  46. return item
  47. }
  48. var (
  49. sessions = make(map[string]*SessionData)
  50. sessionQueue = make(SessionQueue, 0)
  51. )
  52. func sessionExpirer() {
  53. for {
  54. for len(sessionQueue) > 0 && time.Now().After(sessionQueue[0].priority) {
  55. session := heap.Pop(&sessionQueue).(*SessionData)
  56. delete(sessions, session.sid)
  57. }
  58. time.Sleep(time.Second * 5)
  59. }
  60. }
  61. func init() {
  62. go sessionExpirer()
  63. }
  64. func addSession(data *SessionData) {
  65. sessions[data.sid] = data
  66. heap.Push(&sessionQueue, data)
  67. }
  68. func generateHash(password string) (string, error) {
  69. salt := make([]byte, 11)
  70. if _, err := rand.Read(salt); err != nil {
  71. log.Println(err)
  72. return "", errors.New("Couldn't generate random string")
  73. }
  74. passwordBytes := []byte(password)
  75. key := hashPasswordSalt(passwordBytes, salt)
  76. saltStr := base64.StdEncoding.EncodeToString(salt)
  77. keyStr := base64.StdEncoding.EncodeToString(key)
  78. return saltStr + "$" + keyStr, nil
  79. }
  80. func hashOk(password, hashed string) (bool, error) {
  81. parts := strings.Split(hashed, "$")
  82. if len(parts) != 2 {
  83. return false, errors.New("Invalid data stored in database for password")
  84. }
  85. salt, err := base64.StdEncoding.DecodeString(parts[0])
  86. if err != nil {
  87. return false, err
  88. }
  89. passwordHash, err := base64.StdEncoding.DecodeString(parts[1])
  90. if err != nil {
  91. return false, err
  92. }
  93. if len(passwordHash) != 32 {
  94. return false, errors.New("Password hash was not 32 bytes long")
  95. }
  96. key := hashPasswordSalt([]byte(password), salt)
  97. return bytes.Equal(key, passwordHash), nil
  98. }
  99. func hashPasswordSalt(password, salt []byte) []byte {
  100. return pbkdf2.Key(password, salt, 4096, 32, sha1.New)
  101. }
  102. func userOk(db *DB, username, password string) (bool, error) {
  103. hash, err := db.FindHashForUser(username)
  104. if err != nil {
  105. if err.Error() == "sql: no rows in result set" {
  106. return false, nil
  107. } else {
  108. return false, err
  109. }
  110. }
  111. ok, err := hashOk(password, hash)
  112. if err != nil {
  113. return false, err
  114. }
  115. return ok, nil
  116. }
  117. func tryLogin(db *DB, username, password string, longerTime bool) (http.Cookie, error) {
  118. if exists, err := userOk(db, username, password); !exists {
  119. if err != nil {
  120. return http.Cookie{}, err
  121. }
  122. return http.Cookie{},
  123. errors.New("The username or password you entered isn't correct.")
  124. }
  125. sid, err := randString(32)
  126. if err != nil {
  127. return http.Cookie{}, err
  128. }
  129. duration := time.Hour * 1
  130. if longerTime {
  131. duration = time.Hour * 24 * 14
  132. }
  133. loginCookie := http.Cookie{
  134. Name: "id",
  135. Value: sid,
  136. MaxAge: int(duration.Seconds()),
  137. HttpOnly: true,
  138. }
  139. expiration := time.Now().Add(duration)
  140. addSession(&SessionData{sid, username, "", expiration, 0})
  141. return loginCookie, nil
  142. }
  143. func getSession(req *http.Request) (*SessionData, error) {
  144. cookie, err := req.Cookie("id")
  145. if err != nil {
  146. return nil, err
  147. }
  148. session, exists := sessions[cookie.Value]
  149. if !exists {
  150. return nil, errors.New("Session expired from server")
  151. }
  152. return session, nil
  153. }
  154. func randString(size int) (string, error) {
  155. buf := make([]byte, size)
  156. if _, err := rand.Read(buf); err != nil {
  157. log.Println(err)
  158. return "", errors.New("Couldn't generate random string")
  159. }
  160. return base64.URLEncoding.EncodeToString(buf)[:size], nil
  161. }