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.

token.go 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2019 The Gitea Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package models
  6. import (
  7. "crypto/subtle"
  8. "fmt"
  9. "time"
  10. "code.gitea.io/gitea/models/db"
  11. "code.gitea.io/gitea/models/login"
  12. "code.gitea.io/gitea/modules/base"
  13. "code.gitea.io/gitea/modules/setting"
  14. "code.gitea.io/gitea/modules/timeutil"
  15. "code.gitea.io/gitea/modules/util"
  16. gouuid "github.com/google/uuid"
  17. lru "github.com/hashicorp/golang-lru"
  18. )
  19. var successfulAccessTokenCache *lru.Cache
  20. // AccessToken represents a personal access token.
  21. type AccessToken struct {
  22. ID int64 `xorm:"pk autoincr"`
  23. UID int64 `xorm:"INDEX"`
  24. Name string
  25. Token string `xorm:"-"`
  26. TokenHash string `xorm:"UNIQUE"` // sha256 of token
  27. TokenSalt string
  28. TokenLastEight string `xorm:"token_last_eight"`
  29. CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
  30. UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
  31. HasRecentActivity bool `xorm:"-"`
  32. HasUsed bool `xorm:"-"`
  33. }
  34. // AfterLoad is invoked from XORM after setting the values of all fields of this object.
  35. func (t *AccessToken) AfterLoad() {
  36. t.HasUsed = t.UpdatedUnix > t.CreatedUnix
  37. t.HasRecentActivity = t.UpdatedUnix.AddDuration(7*24*time.Hour) > timeutil.TimeStampNow()
  38. }
  39. func init() {
  40. db.RegisterModel(new(AccessToken), func() error {
  41. if setting.SuccessfulTokensCacheSize > 0 {
  42. var err error
  43. successfulAccessTokenCache, err = lru.New(setting.SuccessfulTokensCacheSize)
  44. if err != nil {
  45. return fmt.Errorf("unable to allocate AccessToken cache: %v", err)
  46. }
  47. } else {
  48. successfulAccessTokenCache = nil
  49. }
  50. return nil
  51. })
  52. }
  53. // NewAccessToken creates new access token.
  54. func NewAccessToken(t *AccessToken) error {
  55. salt, err := util.RandomString(10)
  56. if err != nil {
  57. return err
  58. }
  59. t.TokenSalt = salt
  60. t.Token = base.EncodeSha1(gouuid.New().String())
  61. t.TokenHash = login.HashToken(t.Token, t.TokenSalt)
  62. t.TokenLastEight = t.Token[len(t.Token)-8:]
  63. _, err = db.GetEngine(db.DefaultContext).Insert(t)
  64. return err
  65. }
  66. func getAccessTokenIDFromCache(token string) int64 {
  67. if successfulAccessTokenCache == nil {
  68. return 0
  69. }
  70. tInterface, ok := successfulAccessTokenCache.Get(token)
  71. if !ok {
  72. return 0
  73. }
  74. t, ok := tInterface.(int64)
  75. if !ok {
  76. return 0
  77. }
  78. return t
  79. }
  80. // GetAccessTokenBySHA returns access token by given token value
  81. func GetAccessTokenBySHA(token string) (*AccessToken, error) {
  82. if token == "" {
  83. return nil, ErrAccessTokenEmpty{}
  84. }
  85. // A token is defined as being SHA1 sum these are 40 hexadecimal bytes long
  86. if len(token) != 40 {
  87. return nil, ErrAccessTokenNotExist{token}
  88. }
  89. for _, x := range []byte(token) {
  90. if x < '0' || (x > '9' && x < 'a') || x > 'f' {
  91. return nil, ErrAccessTokenNotExist{token}
  92. }
  93. }
  94. lastEight := token[len(token)-8:]
  95. if id := getAccessTokenIDFromCache(token); id > 0 {
  96. token := &AccessToken{
  97. TokenLastEight: lastEight,
  98. }
  99. // Re-get the token from the db in case it has been deleted in the intervening period
  100. has, err := db.GetEngine(db.DefaultContext).ID(id).Get(token)
  101. if err != nil {
  102. return nil, err
  103. }
  104. if has {
  105. return token, nil
  106. }
  107. successfulAccessTokenCache.Remove(token)
  108. }
  109. var tokens []AccessToken
  110. err := db.GetEngine(db.DefaultContext).Table(&AccessToken{}).Where("token_last_eight = ?", lastEight).Find(&tokens)
  111. if err != nil {
  112. return nil, err
  113. } else if len(tokens) == 0 {
  114. return nil, ErrAccessTokenNotExist{token}
  115. }
  116. for _, t := range tokens {
  117. tempHash := login.HashToken(token, t.TokenSalt)
  118. if subtle.ConstantTimeCompare([]byte(t.TokenHash), []byte(tempHash)) == 1 {
  119. if successfulAccessTokenCache != nil {
  120. successfulAccessTokenCache.Add(token, t.ID)
  121. }
  122. return &t, nil
  123. }
  124. }
  125. return nil, ErrAccessTokenNotExist{token}
  126. }
  127. // AccessTokenByNameExists checks if a token name has been used already by a user.
  128. func AccessTokenByNameExists(token *AccessToken) (bool, error) {
  129. return db.GetEngine(db.DefaultContext).Table("access_token").Where("name = ?", token.Name).And("uid = ?", token.UID).Exist()
  130. }
  131. // ListAccessTokensOptions contain filter options
  132. type ListAccessTokensOptions struct {
  133. db.ListOptions
  134. Name string
  135. UserID int64
  136. }
  137. // ListAccessTokens returns a list of access tokens belongs to given user.
  138. func ListAccessTokens(opts ListAccessTokensOptions) ([]*AccessToken, error) {
  139. sess := db.GetEngine(db.DefaultContext).Where("uid=?", opts.UserID)
  140. if len(opts.Name) != 0 {
  141. sess = sess.Where("name=?", opts.Name)
  142. }
  143. sess = sess.Desc("created_unix")
  144. if opts.Page != 0 {
  145. sess = db.SetSessionPagination(sess, &opts)
  146. tokens := make([]*AccessToken, 0, opts.PageSize)
  147. return tokens, sess.Find(&tokens)
  148. }
  149. tokens := make([]*AccessToken, 0, 5)
  150. return tokens, sess.Find(&tokens)
  151. }
  152. // UpdateAccessToken updates information of access token.
  153. func UpdateAccessToken(t *AccessToken) error {
  154. _, err := db.GetEngine(db.DefaultContext).ID(t.ID).AllCols().Update(t)
  155. return err
  156. }
  157. // CountAccessTokens count access tokens belongs to given user by options
  158. func CountAccessTokens(opts ListAccessTokensOptions) (int64, error) {
  159. sess := db.GetEngine(db.DefaultContext).Where("uid=?", opts.UserID)
  160. if len(opts.Name) != 0 {
  161. sess = sess.Where("name=?", opts.Name)
  162. }
  163. return sess.Count(&AccessToken{})
  164. }
  165. // DeleteAccessTokenByID deletes access token by given ID.
  166. func DeleteAccessTokenByID(id, userID int64) error {
  167. cnt, err := db.GetEngine(db.DefaultContext).ID(id).Delete(&AccessToken{
  168. UID: userID,
  169. })
  170. if err != nil {
  171. return err
  172. } else if cnt != 1 {
  173. return ErrAccessTokenNotExist{}
  174. }
  175. return nil
  176. }