|
@@ -1,6 +1,7 @@
|
|
|
package api
|
|
package api
|
|
|
|
|
|
|
|
import (
|
|
import (
|
|
|
|
|
+ "errors"
|
|
|
"runtime"
|
|
"runtime"
|
|
|
|
|
|
|
|
config "github.com/OliveTin/OliveTin/internal/config"
|
|
config "github.com/OliveTin/OliveTin/internal/config"
|
|
@@ -8,6 +9,12 @@ import (
|
|
|
log "github.com/sirupsen/logrus"
|
|
log "github.com/sirupsen/logrus"
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
+var ErrArgon2Busy = errors.New("too many concurrent password operations")
|
|
|
|
|
+
|
|
|
|
|
+const argon2MaxConcurrent = 10
|
|
|
|
|
+
|
|
|
|
|
+var argon2Sem = make(chan struct{}, argon2MaxConcurrent)
|
|
|
|
|
+
|
|
|
var defaultParams = argon2id.Params{
|
|
var defaultParams = argon2id.Params{
|
|
|
Memory: 64 * 1024,
|
|
Memory: 64 * 1024,
|
|
|
Iterations: 4,
|
|
Iterations: 4,
|
|
@@ -17,6 +24,12 @@ var defaultParams = argon2id.Params{
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func CreateHash(password string) (string, error) {
|
|
func CreateHash(password string) (string, error) {
|
|
|
|
|
+ select {
|
|
|
|
|
+ case argon2Sem <- struct{}{}:
|
|
|
|
|
+ defer func() { <-argon2Sem }()
|
|
|
|
|
+ default:
|
|
|
|
|
+ return "", ErrArgon2Busy
|
|
|
|
|
+ }
|
|
|
hash, err := argon2id.CreateHash(password, &defaultParams)
|
|
hash, err := argon2id.CreateHash(password, &defaultParams)
|
|
|
|
|
|
|
|
if err != nil {
|
|
if err != nil {
|
|
@@ -31,30 +44,38 @@ func createHash(password string) (string, error) {
|
|
|
return CreateHash(password)
|
|
return CreateHash(password)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func comparePasswordAndHash(password, hash string) bool {
|
|
|
|
|
|
|
+func comparePasswordAndHash(password, hash string) (bool, error) {
|
|
|
|
|
+ select {
|
|
|
|
|
+ case argon2Sem <- struct{}{}:
|
|
|
|
|
+ defer func() { <-argon2Sem }()
|
|
|
|
|
+ default:
|
|
|
|
|
+ return false, ErrArgon2Busy
|
|
|
|
|
+ }
|
|
|
match, err := argon2id.ComparePasswordAndHash(password, hash)
|
|
match, err := argon2id.ComparePasswordAndHash(password, hash)
|
|
|
|
|
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
log.Errorf("Error comparing password and hash: %v", err)
|
|
log.Errorf("Error comparing password and hash: %v", err)
|
|
|
- return false
|
|
|
|
|
|
|
+ return false, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return match
|
|
|
|
|
|
|
+ return match, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func checkUserPassword(cfg *config.Config, username, password string) bool {
|
|
|
|
|
|
|
+func checkUserPassword(cfg *config.Config, username, password string) (bool, error) {
|
|
|
for _, user := range cfg.AuthLocalUsers.Users {
|
|
for _, user := range cfg.AuthLocalUsers.Users {
|
|
|
if user.Username == username {
|
|
if user.Username == username {
|
|
|
- match := comparePasswordAndHash(password, user.Password)
|
|
|
|
|
-
|
|
|
|
|
|
|
+ match, err := comparePasswordAndHash(password, user.Password)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return false, err
|
|
|
|
|
+ }
|
|
|
if match {
|
|
if match {
|
|
|
- return true
|
|
|
|
|
|
|
+ return true, nil
|
|
|
} else {
|
|
} else {
|
|
|
log.WithFields(log.Fields{
|
|
log.WithFields(log.Fields{
|
|
|
"username": username,
|
|
"username": username,
|
|
|
}).Warn("Password does not match for user")
|
|
}).Warn("Password does not match for user")
|
|
|
|
|
|
|
|
- return false
|
|
|
|
|
|
|
+ return false, nil
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -63,5 +84,5 @@ func checkUserPassword(cfg *config.Config, username, password string) bool {
|
|
|
"username": username,
|
|
"username": username,
|
|
|
}).Warn("Failed to check password for user, as username was not found")
|
|
}).Warn("Failed to check password for user, as username was not found")
|
|
|
|
|
|
|
|
- return false
|
|
|
|
|
|
|
+ return false, nil
|
|
|
}
|
|
}
|