mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-28 11:37:42 +01:00
resolve robot authgen password format issue (#17134)
In some cases, the robot automatically generates passwords that do not meet confidentiality requirements. The fix adds retry for auto generating passwords, and the timeout is 1 minute. The requirement: the secret must longer than 8 chars with at least 1 uppercase letter, 1 lowercase letter and 1 number Signed-off-by: Wang Yan <wangyan@vmware.com>
This commit is contained in:
parent
360689b1b9
commit
bd8d66c68d
@ -3,6 +3,7 @@ package robot
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
@ -12,6 +13,7 @@ import (
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
"github.com/goharbor/harbor/src/lib/log"
|
||||
"github.com/goharbor/harbor/src/lib/q"
|
||||
"github.com/goharbor/harbor/src/lib/retry"
|
||||
"github.com/goharbor/harbor/src/pkg"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
"github.com/goharbor/harbor/src/pkg/project"
|
||||
@ -99,9 +101,10 @@ func (d *controller) Create(ctx context.Context, r *Robot) (int64, string, error
|
||||
expiresAt = time.Now().AddDate(0, 0, duration).Unix()
|
||||
}
|
||||
|
||||
pwd := utils.GenerateRandomString()
|
||||
salt := utils.GenerateRandomString()
|
||||
secret := utils.Encrypt(pwd, salt, utils.SHA256)
|
||||
secret, pwd, salt, err := CreateSec()
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
|
||||
name := r.Name
|
||||
// for the project level robot, set the name pattern as projectname+robotname, and + is a illegal character.
|
||||
@ -360,3 +363,44 @@ func (d *controller) toScope(ctx context.Context, p *Permission) (string, error)
|
||||
}
|
||||
return "", errors.New(nil).WithMessage("unknown robot kind").WithCode(errors.BadRequestCode)
|
||||
}
|
||||
|
||||
func CreateSec(salt ...string) (string, string, string, error) {
|
||||
var secret, pwd string
|
||||
options := []retry.Option{
|
||||
retry.InitialInterval(time.Millisecond * 500),
|
||||
retry.MaxInterval(time.Second * 10),
|
||||
retry.Timeout(time.Minute),
|
||||
retry.Callback(func(err error, sleep time.Duration) {
|
||||
log.Debugf("failed to generate secret for robot, retry after %s : %v", sleep, err)
|
||||
}),
|
||||
}
|
||||
|
||||
if err := retry.Retry(func() error {
|
||||
pwd = utils.GenerateRandomString()
|
||||
if !IsValidSec(pwd) {
|
||||
return errors.New(nil).WithMessage("invalid secret format")
|
||||
}
|
||||
return nil
|
||||
}, options...); err != nil {
|
||||
return "", "", "", errors.Wrap(err, "failed to generate an valid random secret for robot in one minute, please try again")
|
||||
}
|
||||
|
||||
var saltTmp string
|
||||
if len(salt) != 0 {
|
||||
saltTmp = salt[0]
|
||||
} else {
|
||||
saltTmp = utils.GenerateRandomString()
|
||||
}
|
||||
secret = utils.Encrypt(pwd, saltTmp, utils.SHA256)
|
||||
return secret, pwd, saltTmp, nil
|
||||
}
|
||||
|
||||
func IsValidSec(secret string) bool {
|
||||
hasLower := regexp.MustCompile(`[a-z]`)
|
||||
hasUpper := regexp.MustCompile(`[A-Z]`)
|
||||
hasNumber := regexp.MustCompile(`\d`)
|
||||
if len(secret) >= 8 && hasLower.MatchString(secret) && hasUpper.MatchString(secret) && hasNumber.MatchString(secret) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -292,6 +292,20 @@ func (suite *ControllerTestSuite) TestToScope() {
|
||||
|
||||
}
|
||||
|
||||
func (suite *ControllerTestSuite) TestIsValidSec() {
|
||||
sec := "1234abcdABCD"
|
||||
suite.True(IsValidSec(sec))
|
||||
sec = "1234abcd"
|
||||
suite.False(IsValidSec(sec))
|
||||
sec = "123abc"
|
||||
suite.False(IsValidSec(sec))
|
||||
}
|
||||
|
||||
func (suite *ControllerTestSuite) TestCreateSec() {
|
||||
_, pwd, _, err := CreateSec()
|
||||
suite.Nil(err)
|
||||
suite.True(IsValidSec(pwd))
|
||||
}
|
||||
func TestControllerTestSuite(t *testing.T) {
|
||||
suite.Run(t, &ControllerTestSuite{})
|
||||
}
|
||||
|
@ -239,14 +239,17 @@ func (rAPI *robotAPI) RefreshSec(ctx context.Context, params operation.RefreshSe
|
||||
var secret string
|
||||
robotSec := &models.RobotSec{}
|
||||
if params.RobotSec.Secret != "" {
|
||||
if !isValidSec(params.RobotSec.Secret) {
|
||||
if !robot.IsValidSec(params.RobotSec.Secret) {
|
||||
return rAPI.SendError(ctx, errors.New("the secret must longer than 8 chars with at least 1 uppercase letter, 1 lowercase letter and 1 number").WithCode(errors.BadRequestCode))
|
||||
}
|
||||
secret = utils.Encrypt(params.RobotSec.Secret, r.Salt, utils.SHA256)
|
||||
robotSec.Secret = ""
|
||||
} else {
|
||||
pwd := utils.GenerateRandomString()
|
||||
secret = utils.Encrypt(pwd, r.Salt, utils.SHA256)
|
||||
sec, pwd, _, err := robot.CreateSec(r.Salt)
|
||||
if err != nil {
|
||||
return rAPI.SendError(ctx, err)
|
||||
}
|
||||
secret = sec
|
||||
robotSec.Secret = pwd
|
||||
}
|
||||
|
||||
@ -348,16 +351,6 @@ func isValidDuration(d int64) bool {
|
||||
return d >= int64(-1) && d < math.MaxInt32
|
||||
}
|
||||
|
||||
func isValidSec(sec string) bool {
|
||||
hasLower := regexp.MustCompile(`[a-z]`)
|
||||
hasUpper := regexp.MustCompile(`[A-Z]`)
|
||||
hasNumber := regexp.MustCompile(`\d`)
|
||||
if len(sec) >= 8 && hasLower.MatchString(sec) && hasUpper.MatchString(sec) && hasNumber.MatchString(sec) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// validateName validates the robot name, especially '+' cannot be a valid character
|
||||
func validateName(name string) error {
|
||||
robotNameReg := `^[a-z0-9]+(?:[._-][a-z0-9]+)*$`
|
||||
|
Loading…
Reference in New Issue
Block a user