Skip to content

Commit

Permalink
Replace comma in username to avoid casbin issue
Browse files Browse the repository at this point in the history
   Check username when creating user by API
   Replace comma with underscore in username for OnboardUser
   Fixes #19356

Signed-off-by: stonezdj <daojunz@vmware.com>
  • Loading branch information
stonezdj committed Oct 31, 2023
1 parent 58557d3 commit 1da6af1
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 13 deletions.
2 changes: 2 additions & 0 deletions src/common/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,4 +228,6 @@ const (
ExecutionStatusRefreshIntervalSeconds = "execution_status_refresh_interval_seconds"
// QuotaUpdateProvider is the provider for updating quota, currently support Redis and DB
QuotaUpdateProvider = "quota_update_provider"
// IllegalCharsInUsername is the illegal chars in username
IllegalCharsInUsername = `,"~#%$`
)
10 changes: 0 additions & 10 deletions src/common/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,16 +247,6 @@ func IsIllegalLength(s string, min int, max int) bool {
return (len(s) < min || len(s) > max)
}

// IsContainIllegalChar ...
func IsContainIllegalChar(s string, illegalChar []string) bool {
for _, c := range illegalChar {
if strings.Contains(s, c) {
return true
}
}
return false
}

// ParseJSONInt ...
func ParseJSONInt(value interface{}) (int, bool) {
switch v := value.(type) {
Expand Down
4 changes: 2 additions & 2 deletions src/core/controllers/oidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,8 @@ func (oc *OIDCController) Onboard() {
oc.SendBadRequestError(errors.New("username with illegal length"))
return
}
if utils.IsContainIllegalChar(username, []string{",", "~", "#", "$", "%"}) {
oc.SendBadRequestError(errors.New("username contains illegal characters"))
if strings.ContainsAny(username, common.IllegalCharsInUsername) {
oc.SendBadRequestError(errors.Errorf("username %v contains illegal characters: %v", username, common.IllegalCharsInUsername))
return
}

Expand Down
6 changes: 6 additions & 0 deletions src/pkg/member/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ type Manager interface {
SearchMemberByName(ctx context.Context, projectID int64, entityName string) ([]*models.Member, error)
// DeleteMemberByUserID delete project member by user id
DeleteMemberByUserID(ctx context.Context, uid int) error
// DeleteMemberByProjectID delete project member by project id
DeleteMemberByProjectID(ctx context.Context, projectID int64) error
// GetTotalOfProjectMembers get the total amount of project members
GetTotalOfProjectMembers(ctx context.Context, projectID int64, query *q.Query, roles ...int) (int, error)
// ListRoles list project roles
Expand Down Expand Up @@ -102,6 +104,10 @@ func (m *manager) DeleteMemberByUserID(ctx context.Context, uid int) error {
return m.dao.DeleteProjectMemberByUserID(ctx, uid)
}

func (m *manager) DeleteMemberByProjectID(ctx context.Context, projectID int64) error {
return m.dao.DeleteProjectMemberByProjectID(ctx, projectID)
}

// NewManager ...
func NewManager() Manager {
return &manager{dao: dao.New()}
Expand Down
4 changes: 4 additions & 0 deletions src/pkg/user/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,10 @@ func (m *manager) SetSysAdminFlag(ctx context.Context, id int, admin bool) error

func (m *manager) Create(ctx context.Context, user *commonmodels.User) (int, error) {
injectPasswd(user, user.Password)
// replace comma in username with underscore to avoid #19356
// if the username conflict with existing username,
// it will return error and have to update to a new username manually with sql
user.Username = strings.Replace(user.Username, ",", "_", -1)
return m.dao.Create(ctx, user)
}

Expand Down
24 changes: 24 additions & 0 deletions src/pkg/user/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,27 @@ func TestInjectPasswd(t *testing.T) {
assert.Equal(t, "sha256", u.PasswordVersion)
assert.Equal(t, utils.Encrypt(p, u.Salt, "sha256"), u.Password)
}

func (m *mgrTestSuite) TestCreate() {
m.dao.On("Create", mock.Anything, testifymock.Anything).Return(3, nil)
u := &models.User{
Username: "test",
Email: "test@example.com",
Realname: "test",
}
id, err := m.mgr.Create(context.Background(), u)
m.Nil(err)
m.Equal(3, id)
m.Equal(u.Username, "test")

u2 := &models.User{
Username: "test,test",
Email: "test@example.com",
Realname: "test",
}

id, err = m.mgr.Create(context.Background(), u2)
m.Nil(err)
m.Equal(3, id)
m.Equal(u2.Username, "test_test", "username should be sanitized")
}
10 changes: 9 additions & 1 deletion src/server/v2.0/handler/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -495,10 +495,18 @@ func validateUserProfile(user *commonmodels.User) error {
return errors.BadRequestError(nil).WithMessage("realname with illegal length")
}

if utils.IsContainIllegalChar(user.Realname, []string{",", "~", "#", "$", "%"}) {
if strings.ContainsAny(user.Realname, common.IllegalCharsInUsername) {
return errors.BadRequestError(nil).WithMessage("realname contains illegal characters")
}

if utils.IsIllegalLength(user.Username, 1, 255) {
return errors.BadRequestError(nil).WithMessage("usernamae with illegal length")
}

if strings.ContainsAny(user.Username, common.IllegalCharsInUsername) {
return errors.BadRequestError(nil).WithMessage("username contains illegal characters")
}

if utils.IsIllegalLength(user.Comment, -1, 30) {
return errors.BadRequestError(nil).WithMessage("comment with illegal length")
}
Expand Down
28 changes: 28 additions & 0 deletions src/server/v2.0/handler/user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package handler

import (
"context"
"fmt"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -113,3 +114,30 @@ func (uts *UserTestSuite) TestGetRandomSecret() {
func TestUserTestSuite(t *testing.T) {
suite.Run(t, &UserTestSuite{})
}

func Test_validateUserProfile(t *testing.T) {
tooLongUsername := "mike012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789mike012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789mike012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
type args struct {
user *commonmodels.User
}
tests := []struct {
name string
args args
wantErr assert.ErrorAssertionFunc
}{
{"normal_test", args{&commonmodels.User{Username: "mike", Realname: "mike", Email: "mike@example.com"}}, assert.NoError},
{"illegall_username_,", args{&commonmodels.User{Username: "mike,mike", Realname: "mike", Email: "mike@example.com"}}, assert.Error},
{"illegall_username_$", args{&commonmodels.User{Username: "mike$mike", Realname: "mike", Email: "mike@example.com"}}, assert.Error},
{"illegall_username_%", args{&commonmodels.User{Username: "mike%mike", Realname: "mike", Email: "mike@example.com"}}, assert.Error},
{"illegall_username_#", args{&commonmodels.User{Username: "mike#mike", Realname: "mike", Email: "mike@example.com"}}, assert.Error},
{"illegall_realname", args{&commonmodels.User{Username: "mike", Realname: "mike,mike", Email: "mike@example.com"}}, assert.Error},
{"username_too_long", args{&commonmodels.User{Username: tooLongUsername, Realname: "mike", Email: "mike@example.com"}}, assert.Error},
{"invalid_email", args{&commonmodels.User{Username: "mike", Realname: "mike", Email: "mike#example.com"}}, assert.Error},
{"invalid_comment", args{&commonmodels.User{Username: "mike", Realname: "mike", Email: "mike@example.com", Comment: tooLongUsername}}, assert.Error},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.wantErr(t, validateUserProfile(tt.args.user), fmt.Sprintf("validateUserProfile(%v)", tt.args.user))
})
}
}

0 comments on commit 1da6af1

Please sign in to comment.