diff --git a/src/server/v2.0/handler/user.go b/src/server/v2.0/handler/user.go index 7690d0a56..13a73cb47 100644 --- a/src/server/v2.0/handler/user.go +++ b/src/server/v2.0/handler/user.go @@ -19,6 +19,7 @@ import ( "fmt" "regexp" "strings" + "time" "github.com/go-openapi/runtime/middleware" @@ -35,6 +36,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/permission/types" "github.com/goharbor/harbor/src/server/v2.0/handler/model" "github.com/goharbor/harbor/src/server/v2.0/models" @@ -61,6 +63,13 @@ func (u *usersAPI) SetCliSecret(ctx context.Context, params operation.SetCliSecr if err := u.requireForCLISecret(ctx, uid); err != nil { return u.SendError(ctx, err) } + if params.Secret.Secret == "" { + rSec, err := getRandomSecret() + if err != nil { + return u.SendError(ctx, err) + } + params.Secret.Secret = rSec + } if err := requireValidSecret(params.Secret.Secret); err != nil { return u.SendError(ctx, err) } @@ -444,6 +453,29 @@ func requireValidSecret(in string) error { return errors.BadRequestError(nil).WithMessage("the password or secret must be longer than 8 chars with at least 1 uppercase letter, 1 lowercase letter and 1 number") } +func getRandomSecret() (string, error) { + var cliSecret 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 cli, retry after %s : %v", sleep, err) + }), + } + + if err := retry.Retry(func() error { + cliSecret = utils.GenerateRandomStringWithLen(9) + if err := requireValidSecret(cliSecret); err != nil { + return errors.New(nil).WithMessage("invalid cli secret format") + } + return nil + }, options...); err != nil { + return "", errors.Wrap(err, "failed to generate an valid random secret for cli in one minute, please try again") + } + return cliSecret, nil +} + func validateUserProfile(user *commonmodels.User) error { if len(user.Email) > 0 { if m, _ := regexp.MatchString(`^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$`, user.Email); !m { diff --git a/src/server/v2.0/handler/user_test.go b/src/server/v2.0/handler/user_test.go index bf242c0ff..cc7e11c39 100644 --- a/src/server/v2.0/handler/user_test.go +++ b/src/server/v2.0/handler/user_test.go @@ -90,6 +90,14 @@ func (uts *UserTestSuite) TestUpdateUserPassword() { } } +func (uts *UserTestSuite) TestGetRandomSecret() { + for i := 1; i < 5; i++ { + rSec, err := getRandomSecret() + uts.NoError(err) + uts.NoError(requireValidSecret(rSec)) + } +} + func TestUserTestSuite(t *testing.T) { suite.Run(t, &UserTestSuite{}) }