aboutsummaryrefslogtreecommitdiffstats
path: root/models
diff options
context:
space:
mode:
authorzeripath <art27@cantab.net>2021-08-17 19:30:42 +0100
committerGitHub <noreply@github.com>2021-08-17 14:30:42 -0400
commite0853d4a21bd1c718f23a3cac6148f5063e4e810 (patch)
tree62fed8fe5f9356ee82dc7ad1ea2429b84da0540d /models
parent274aeb3a9e13da47a925eabe0e43ed0b1424d02a (diff)
downloadgitea-e0853d4a21bd1c718f23a3cac6148f5063e4e810.tar.gz
gitea-e0853d4a21bd1c718f23a3cac6148f5063e4e810.zip
Add API Token Cache (#16547)
One of the issues holding back performance of the API is the problem of hashing. Whilst banning BASIC authentication with passwords will help, the API Token scheme still requires a PBKDF2 hash - which means that heavy API use (using Tokens) can still cause enormous numbers of hash computations. A slight solution to this whilst we consider moving to using JWT based tokens and/or a session orientated solution is to simply cache the successful tokens. This has some security issues but this should be balanced by the security issues of load from hashing. Related #14668 Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Diffstat (limited to 'models')
-rwxr-xr-xmodels/models.go10
-rw-r--r--models/token.go41
2 files changed, 50 insertions, 1 deletions
diff --git a/models/models.go b/models/models.go
index c843302296..4e1448241a 100755
--- a/models/models.go
+++ b/models/models.go
@@ -17,6 +17,7 @@ import (
// Needed for the MySQL driver
_ "github.com/go-sql-driver/mysql"
+ lru "github.com/hashicorp/golang-lru"
"xorm.io/xorm"
"xorm.io/xorm/names"
"xorm.io/xorm/schemas"
@@ -234,6 +235,15 @@ func NewEngine(ctx context.Context, migrateFunc func(*xorm.Engine) error) (err e
return fmt.Errorf("sync database struct error: %v", err)
}
+ if setting.SuccessfulTokensCacheSize > 0 {
+ successfulAccessTokenCache, err = lru.New(setting.SuccessfulTokensCacheSize)
+ if err != nil {
+ return fmt.Errorf("unable to allocate AccessToken cache: %v", err)
+ }
+ } else {
+ successfulAccessTokenCache = nil
+ }
+
return nil
}
diff --git a/models/token.go b/models/token.go
index 8e1f91d43f..9baa763f1c 100644
--- a/models/token.go
+++ b/models/token.go
@@ -14,8 +14,11 @@ import (
"code.gitea.io/gitea/modules/util"
gouuid "github.com/google/uuid"
+ lru "github.com/hashicorp/golang-lru"
)
+var successfulAccessTokenCache *lru.Cache
+
// AccessToken represents a personal access token.
type AccessToken struct {
ID int64 `xorm:"pk autoincr"`
@@ -52,6 +55,21 @@ func NewAccessToken(t *AccessToken) error {
return err
}
+func getAccessTokenIDFromCache(token string) int64 {
+ if successfulAccessTokenCache == nil {
+ return 0
+ }
+ tInterface, ok := successfulAccessTokenCache.Get(token)
+ if !ok {
+ return 0
+ }
+ t, ok := tInterface.(int64)
+ if !ok {
+ return 0
+ }
+ return t
+}
+
// GetAccessTokenBySHA returns access token by given token value
func GetAccessTokenBySHA(token string) (*AccessToken, error) {
if token == "" {
@@ -66,17 +84,38 @@ func GetAccessTokenBySHA(token string) (*AccessToken, error) {
return nil, ErrAccessTokenNotExist{token}
}
}
- var tokens []AccessToken
+
lastEight := token[len(token)-8:]
+
+ if id := getAccessTokenIDFromCache(token); id > 0 {
+ token := &AccessToken{
+ TokenLastEight: lastEight,
+ }
+ // Re-get the token from the db in case it has been deleted in the intervening period
+ has, err := x.ID(id).Get(token)
+ if err != nil {
+ return nil, err
+ }
+ if has {
+ return token, nil
+ }
+ successfulAccessTokenCache.Remove(token)
+ }
+
+ var tokens []AccessToken
err := x.Table(&AccessToken{}).Where("token_last_eight = ?", lastEight).Find(&tokens)
if err != nil {
return nil, err
} else if len(tokens) == 0 {
return nil, ErrAccessTokenNotExist{token}
}
+
for _, t := range tokens {
tempHash := hashToken(token, t.TokenSalt)
if subtle.ConstantTimeCompare([]byte(t.TokenHash), []byte(tempHash)) == 1 {
+ if successfulAccessTokenCache != nil {
+ successfulAccessTokenCache.Add(token, t.ID)
+ }
return &t, nil
}
}