gitea源码

access_token.go 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2019 The Gitea Authors. All rights reserved.
  3. // SPDX-License-Identifier: MIT
  4. package auth
  5. import (
  6. "context"
  7. "crypto/subtle"
  8. "encoding/hex"
  9. "fmt"
  10. "time"
  11. "code.gitea.io/gitea/models/db"
  12. "code.gitea.io/gitea/modules/setting"
  13. "code.gitea.io/gitea/modules/timeutil"
  14. "code.gitea.io/gitea/modules/util"
  15. lru "github.com/hashicorp/golang-lru/v2"
  16. "xorm.io/builder"
  17. )
  18. // ErrAccessTokenNotExist represents a "AccessTokenNotExist" kind of error.
  19. type ErrAccessTokenNotExist struct {
  20. Token string
  21. }
  22. // IsErrAccessTokenNotExist checks if an error is a ErrAccessTokenNotExist.
  23. func IsErrAccessTokenNotExist(err error) bool {
  24. _, ok := err.(ErrAccessTokenNotExist)
  25. return ok
  26. }
  27. func (err ErrAccessTokenNotExist) Error() string {
  28. return fmt.Sprintf("access token does not exist [sha: %s]", err.Token)
  29. }
  30. func (err ErrAccessTokenNotExist) Unwrap() error {
  31. return util.ErrNotExist
  32. }
  33. // ErrAccessTokenEmpty represents a "AccessTokenEmpty" kind of error.
  34. type ErrAccessTokenEmpty struct{}
  35. // IsErrAccessTokenEmpty checks if an error is a ErrAccessTokenEmpty.
  36. func IsErrAccessTokenEmpty(err error) bool {
  37. _, ok := err.(ErrAccessTokenEmpty)
  38. return ok
  39. }
  40. func (err ErrAccessTokenEmpty) Error() string {
  41. return "access token is empty"
  42. }
  43. func (err ErrAccessTokenEmpty) Unwrap() error {
  44. return util.ErrInvalidArgument
  45. }
  46. var successfulAccessTokenCache *lru.Cache[string, any]
  47. // AccessToken represents a personal access token.
  48. type AccessToken struct {
  49. ID int64 `xorm:"pk autoincr"`
  50. UID int64 `xorm:"INDEX"`
  51. Name string
  52. Token string `xorm:"-"`
  53. TokenHash string `xorm:"UNIQUE"` // sha256 of token
  54. TokenSalt string
  55. TokenLastEight string `xorm:"INDEX token_last_eight"`
  56. Scope AccessTokenScope
  57. CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
  58. UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
  59. HasRecentActivity bool `xorm:"-"`
  60. HasUsed bool `xorm:"-"`
  61. }
  62. // AfterLoad is invoked from XORM after setting the values of all fields of this object.
  63. func (t *AccessToken) AfterLoad() {
  64. t.HasUsed = t.UpdatedUnix > t.CreatedUnix
  65. t.HasRecentActivity = t.UpdatedUnix.AddDuration(7*24*time.Hour) > timeutil.TimeStampNow()
  66. }
  67. func init() {
  68. db.RegisterModel(new(AccessToken), func() error {
  69. if setting.SuccessfulTokensCacheSize > 0 {
  70. var err error
  71. successfulAccessTokenCache, err = lru.New[string, any](setting.SuccessfulTokensCacheSize)
  72. if err != nil {
  73. return fmt.Errorf("unable to allocate AccessToken cache: %w", err)
  74. }
  75. } else {
  76. successfulAccessTokenCache = nil
  77. }
  78. return nil
  79. })
  80. }
  81. // NewAccessToken creates new access token.
  82. func NewAccessToken(ctx context.Context, t *AccessToken) error {
  83. salt, err := util.CryptoRandomString(10)
  84. if err != nil {
  85. return err
  86. }
  87. token, err := util.CryptoRandomBytes(20)
  88. if err != nil {
  89. return err
  90. }
  91. t.TokenSalt = salt
  92. t.Token = hex.EncodeToString(token)
  93. t.TokenHash = HashToken(t.Token, t.TokenSalt)
  94. t.TokenLastEight = t.Token[len(t.Token)-8:]
  95. _, err = db.GetEngine(ctx).Insert(t)
  96. return err
  97. }
  98. // DisplayPublicOnly whether to display this as a public-only token.
  99. func (t *AccessToken) DisplayPublicOnly() bool {
  100. publicOnly, err := t.Scope.PublicOnly()
  101. if err != nil {
  102. return false
  103. }
  104. return publicOnly
  105. }
  106. func getAccessTokenIDFromCache(token string) int64 {
  107. if successfulAccessTokenCache == nil {
  108. return 0
  109. }
  110. tInterface, ok := successfulAccessTokenCache.Get(token)
  111. if !ok {
  112. return 0
  113. }
  114. t, ok := tInterface.(int64)
  115. if !ok {
  116. return 0
  117. }
  118. return t
  119. }
  120. // GetAccessTokenBySHA returns access token by given token value
  121. func GetAccessTokenBySHA(ctx context.Context, token string) (*AccessToken, error) {
  122. if token == "" {
  123. return nil, ErrAccessTokenEmpty{}
  124. }
  125. // A token is defined as being SHA1 sum these are 40 hexadecimal bytes long
  126. if len(token) != 40 {
  127. return nil, ErrAccessTokenNotExist{token}
  128. }
  129. for _, x := range []byte(token) {
  130. if x < '0' || (x > '9' && x < 'a') || x > 'f' {
  131. return nil, ErrAccessTokenNotExist{token}
  132. }
  133. }
  134. lastEight := token[len(token)-8:]
  135. if id := getAccessTokenIDFromCache(token); id > 0 {
  136. accessToken := &AccessToken{
  137. TokenLastEight: lastEight,
  138. }
  139. // Re-get the token from the db in case it has been deleted in the intervening period
  140. has, err := db.GetEngine(ctx).ID(id).Get(accessToken)
  141. if err != nil {
  142. return nil, err
  143. }
  144. if has {
  145. return accessToken, nil
  146. }
  147. successfulAccessTokenCache.Remove(token)
  148. }
  149. var tokens []AccessToken
  150. err := db.GetEngine(ctx).Table(&AccessToken{}).Where("token_last_eight = ?", lastEight).Find(&tokens)
  151. if err != nil {
  152. return nil, err
  153. } else if len(tokens) == 0 {
  154. return nil, ErrAccessTokenNotExist{token}
  155. }
  156. for _, t := range tokens {
  157. tempHash := HashToken(token, t.TokenSalt)
  158. if subtle.ConstantTimeCompare([]byte(t.TokenHash), []byte(tempHash)) == 1 {
  159. if successfulAccessTokenCache != nil {
  160. successfulAccessTokenCache.Add(token, t.ID)
  161. }
  162. return &t, nil
  163. }
  164. }
  165. return nil, ErrAccessTokenNotExist{token}
  166. }
  167. // AccessTokenByNameExists checks if a token name has been used already by a user.
  168. func AccessTokenByNameExists(ctx context.Context, token *AccessToken) (bool, error) {
  169. return db.GetEngine(ctx).Table("access_token").Where("name = ?", token.Name).And("uid = ?", token.UID).Exist()
  170. }
  171. // ListAccessTokensOptions contain filter options
  172. type ListAccessTokensOptions struct {
  173. db.ListOptions
  174. Name string
  175. UserID int64
  176. }
  177. func (opts ListAccessTokensOptions) ToConds() builder.Cond {
  178. cond := builder.NewCond()
  179. // user id is required, otherwise it will return all result which maybe a possible bug
  180. cond = cond.And(builder.Eq{"uid": opts.UserID})
  181. if len(opts.Name) > 0 {
  182. cond = cond.And(builder.Eq{"name": opts.Name})
  183. }
  184. return cond
  185. }
  186. func (opts ListAccessTokensOptions) ToOrders() string {
  187. return "created_unix DESC"
  188. }
  189. // UpdateAccessToken updates information of access token.
  190. func UpdateAccessToken(ctx context.Context, t *AccessToken) error {
  191. _, err := db.GetEngine(ctx).ID(t.ID).AllCols().Update(t)
  192. return err
  193. }
  194. // DeleteAccessTokenByID deletes access token by given ID.
  195. func DeleteAccessTokenByID(ctx context.Context, id, userID int64) error {
  196. cnt, err := db.GetEngine(ctx).ID(id).Delete(&AccessToken{
  197. UID: userID,
  198. })
  199. if err != nil {
  200. return err
  201. } else if cnt != 1 {
  202. return ErrAccessTokenNotExist{}
  203. }
  204. return nil
  205. }