Add check when adding project member (#17701)

Signed-off-by: stonezdj <stonezdj@gmail.com>

Signed-off-by: stonezdj <stonezdj@gmail.com>
This commit is contained in:
stonezdj(Daojun Zhang) 2022-10-28 15:06:27 +08:00 committed by GitHub
parent 6f4d84ecdb
commit 3050e856d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 507 additions and 4 deletions

View File

@ -75,14 +75,15 @@ var ErrDuplicateProjectMember = errors.ConflictError(nil).WithMessage("The proje
var ErrInvalidRole = errors.BadRequestError(nil).WithMessage("Failed to update project member, role is not in 1,2,3")
type controller struct {
userManager user.Manager
mgr member.Manager
projectMgr project.Manager
userManager user.Manager
mgr member.Manager
projectMgr project.Manager
groupManager usergroup.Manager
}
// NewController ...
func NewController() Controller {
return &controller{mgr: member.Mgr, projectMgr: pkg.ProjectMgr, userManager: user.New()}
return &controller{mgr: member.Mgr, projectMgr: pkg.ProjectMgr, userManager: user.New(), groupManager: usergroup.Mgr}
}
func (c *controller) Count(ctx context.Context, projectNameOrID interface{}, query *q.Query) (int, error) {
@ -129,9 +130,23 @@ func (c *controller) Create(ctx context.Context, projectNameOrID interface{}, re
member.EntityType = common.GroupMember
if req.MemberUser.UserID > 0 {
user, err := c.userManager.Get(ctx, req.MemberUser.UserID)
if err != nil {
return 0, errors.BadRequestError(nil).WithMessage("Failed to get user %d: %v", req.MemberUser.UserID, err)
}
if user == nil {
return 0, errors.BadRequestError(nil).WithMessage("User %d not found", req.MemberUser.UserID)
}
member.EntityID = req.MemberUser.UserID
member.EntityType = common.UserMember
} else if req.MemberGroup.ID > 0 {
g, err := c.groupManager.Get(ctx, req.MemberGroup.ID)
if err != nil {
return 0, errors.BadRequestError(nil).WithMessage("Failed to get group %d: %v", req.MemberGroup.ID, err)
}
if g == nil {
return 0, errors.BadRequestError(nil).WithMessage("Group %d not found", req.MemberGroup.ID)
}
member.EntityID = req.MemberGroup.ID
} else if len(req.MemberUser.Username) > 0 {
// If username is provided, search userid by username

View File

@ -13,3 +13,88 @@
// limitations under the License.
package member
import (
"fmt"
"testing"
"github.com/stretchr/testify/suite"
comModels "github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/pkg/member"
"github.com/goharbor/harbor/src/pkg/project"
"github.com/goharbor/harbor/src/pkg/project/models"
"github.com/goharbor/harbor/src/pkg/user"
"github.com/goharbor/harbor/src/pkg/usergroup"
modelGroup "github.com/goharbor/harbor/src/pkg/usergroup/model"
"github.com/goharbor/harbor/src/testing/mock"
mockMember "github.com/goharbor/harbor/src/testing/pkg/member"
mockProject "github.com/goharbor/harbor/src/testing/pkg/project"
mockUser "github.com/goharbor/harbor/src/testing/pkg/user"
mockUsergroup "github.com/goharbor/harbor/src/testing/pkg/usergroup"
)
type MemberControllerTestSuite struct {
suite.Suite
userManager user.Manager
memberManager member.Manager
projectMgr project.Manager
groupManager usergroup.Manager
controller *controller
}
func (suite *MemberControllerTestSuite) SetupSuite() {
suite.userManager = &mockUser.Manager{}
suite.memberManager = &mockMember.Manager{}
suite.projectMgr = &mockProject.Manager{}
suite.groupManager = &mockUsergroup.Manager{}
suite.controller = &controller{
userManager: suite.userManager,
mgr: suite.memberManager,
projectMgr: suite.projectMgr,
groupManager: suite.groupManager,
}
}
func (suite *MemberControllerTestSuite) TearDownSuite() {
}
func (suite *MemberControllerTestSuite) TestAddProjectMemberWithUser() {
mock.OnAnything(suite.projectMgr, "Get").Return(&models.Project{
ProjectID: 1,
}, nil)
suite.userManager.(*mockUser.Manager).On("Get", mock.Anything, 1).Return(nil, fmt.Errorf("user not found"))
_, err := suite.controller.Create(nil, 1, Request{MemberUser: User{UserID: 1}})
suite.Error(err)
_, err = suite.controller.Create(nil, 1, Request{MemberUser: User{UserID: 1}})
suite.Error(err)
suite.userManager.(*mockUser.Manager).On("Get", mock.Anything, 2).Return(&comModels.User{UserID: 2, Username: "mike"}, nil)
suite.memberManager.(*mockMember.Manager).On("Add", mock.Anything, 1, 2).Return(nil)
mock.OnAnything(suite.memberManager, "List").Return(nil, nil)
mock.OnAnything(suite.memberManager, "AddProjectMember").Return(0, nil)
_, err = suite.controller.Create(nil, 1, Request{MemberUser: User{UserID: 2}, Role: 1})
suite.NoError(err)
}
func (suite *MemberControllerTestSuite) TestAddProjectMemberWithUserGroup() {
mock.OnAnything(suite.projectMgr, "Get").Return(&models.Project{
ProjectID: 1,
}, nil)
suite.groupManager.(*mockUsergroup.Manager).On("Get", mock.Anything, 1).Return(nil, fmt.Errorf("user group not found"))
_, err := suite.controller.Create(nil, 1, Request{MemberGroup: UserGroup{ID: 1}})
suite.Error(err)
suite.groupManager.(*mockUsergroup.Manager).On("Get", mock.Anything, 1).Return(nil, fmt.Errorf("group not found"))
_, err = suite.controller.Create(nil, 1, Request{MemberGroup: UserGroup{ID: 1}})
suite.Error(err)
suite.groupManager.(*mockUsergroup.Manager).On("Get", mock.Anything, 2).Return(&modelGroup.UserGroup{ID: 2, GroupName: "group1"}, nil)
suite.memberManager.(*mockMember.Manager).On("Add", mock.Anything, 1, 2).Return(nil)
mock.OnAnything(suite.memberManager, "List").Return(nil, nil)
mock.OnAnything(suite.memberManager, "AddProjectMember").Return(0, nil)
_, err = suite.controller.Create(nil, 1, Request{MemberGroup: UserGroup{ID: 2}, Role: 1})
suite.NoError(err)
}
func TestMemberControllerTestSuite(t *testing.T) {
suite.Run(t, &MemberControllerTestSuite{})
}

View File

@ -0,0 +1,216 @@
// Code generated by mockery v2.14.0. DO NOT EDIT.
package member
import (
context "context"
mock "github.com/stretchr/testify/mock"
models "github.com/goharbor/harbor/src/pkg/member/models"
q "github.com/goharbor/harbor/src/lib/q"
)
// Manager is an autogenerated mock type for the Manager type
type Manager struct {
mock.Mock
}
// AddProjectMember provides a mock function with given fields: ctx, _a1
func (_m *Manager) AddProjectMember(ctx context.Context, _a1 models.Member) (int, error) {
ret := _m.Called(ctx, _a1)
var r0 int
if rf, ok := ret.Get(0).(func(context.Context, models.Member) int); ok {
r0 = rf(ctx, _a1)
} else {
r0 = ret.Get(0).(int)
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, models.Member) error); ok {
r1 = rf(ctx, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Delete provides a mock function with given fields: ctx, projectID, memberID
func (_m *Manager) Delete(ctx context.Context, projectID int64, memberID int) error {
ret := _m.Called(ctx, projectID, memberID)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, int64, int) error); ok {
r0 = rf(ctx, projectID, memberID)
} else {
r0 = ret.Error(0)
}
return r0
}
// DeleteMemberByUserID provides a mock function with given fields: ctx, uid
func (_m *Manager) DeleteMemberByUserID(ctx context.Context, uid int) error {
ret := _m.Called(ctx, uid)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, int) error); ok {
r0 = rf(ctx, uid)
} else {
r0 = ret.Error(0)
}
return r0
}
// Get provides a mock function with given fields: ctx, projectID, memberID
func (_m *Manager) Get(ctx context.Context, projectID int64, memberID int) (*models.Member, error) {
ret := _m.Called(ctx, projectID, memberID)
var r0 *models.Member
if rf, ok := ret.Get(0).(func(context.Context, int64, int) *models.Member); ok {
r0 = rf(ctx, projectID, memberID)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.Member)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int64, int) error); ok {
r1 = rf(ctx, projectID, memberID)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetTotalOfProjectMembers provides a mock function with given fields: ctx, projectID, query, roles
func (_m *Manager) GetTotalOfProjectMembers(ctx context.Context, projectID int64, query *q.Query, roles ...int) (int, error) {
_va := make([]interface{}, len(roles))
for _i := range roles {
_va[_i] = roles[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, projectID, query)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 int
if rf, ok := ret.Get(0).(func(context.Context, int64, *q.Query, ...int) int); ok {
r0 = rf(ctx, projectID, query, roles...)
} else {
r0 = ret.Get(0).(int)
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int64, *q.Query, ...int) error); ok {
r1 = rf(ctx, projectID, query, roles...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// List provides a mock function with given fields: ctx, queryMember, query
func (_m *Manager) List(ctx context.Context, queryMember models.Member, query *q.Query) ([]*models.Member, error) {
ret := _m.Called(ctx, queryMember, query)
var r0 []*models.Member
if rf, ok := ret.Get(0).(func(context.Context, models.Member, *q.Query) []*models.Member); ok {
r0 = rf(ctx, queryMember, query)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*models.Member)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, models.Member, *q.Query) error); ok {
r1 = rf(ctx, queryMember, query)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ListRoles provides a mock function with given fields: ctx, user, projectID
func (_m *Manager) ListRoles(ctx context.Context, user *models.User, projectID int64) ([]int, error) {
ret := _m.Called(ctx, user, projectID)
var r0 []int
if rf, ok := ret.Get(0).(func(context.Context, *models.User, int64) []int); ok {
r0 = rf(ctx, user, projectID)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]int)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *models.User, int64) error); ok {
r1 = rf(ctx, user, projectID)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// SearchMemberByName provides a mock function with given fields: ctx, projectID, entityName
func (_m *Manager) SearchMemberByName(ctx context.Context, projectID int64, entityName string) ([]*models.Member, error) {
ret := _m.Called(ctx, projectID, entityName)
var r0 []*models.Member
if rf, ok := ret.Get(0).(func(context.Context, int64, string) []*models.Member); ok {
r0 = rf(ctx, projectID, entityName)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*models.Member)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int64, string) error); ok {
r1 = rf(ctx, projectID, entityName)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// UpdateRole provides a mock function with given fields: ctx, projectID, pmID, role
func (_m *Manager) UpdateRole(ctx context.Context, projectID int64, pmID int, role int) error {
ret := _m.Called(ctx, projectID, pmID, role)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, int64, int, int) error); ok {
r0 = rf(ctx, projectID, pmID, role)
} else {
r0 = ret.Error(0)
}
return r0
}
type mockConstructorTestingTNewManager interface {
mock.TestingT
Cleanup(func())
}
// NewManager creates a new instance of Manager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewManager(t mockConstructorTestingTNewManager) *Manager {
mock := &Manager{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -66,3 +66,5 @@ package pkg
//go:generate mockery --case snake --dir ../../pkg/scan/export --name Manager --output ./scan/export --outpkg export
//go:generate mockery --case snake --dir ../../pkg/scan/export --name ArtifactDigestCalculator --output ./scan/export --outpkg export
//go:generate mockery --case snake --dir ../../pkg/registry --name Client --output ./registry --outpkg registry --filename fake_registry_client.go
//go:generate mockery --case snake --dir ../../pkg/member --name Manager --output ./member --outpkg member --filename fake_member_manager.go
//go:generate mockery --case snake --dir ../../pkg/usergroup --name Manager --output ./usergroup --outpkg usergroup --filename fake_usergroup_manager.go

View File

@ -0,0 +1,185 @@
// Code generated by mockery v2.14.0. DO NOT EDIT.
package usergroup
import (
context "context"
model "github.com/goharbor/harbor/src/pkg/usergroup/model"
mock "github.com/stretchr/testify/mock"
q "github.com/goharbor/harbor/src/lib/q"
)
// Manager is an autogenerated mock type for the Manager type
type Manager struct {
mock.Mock
}
// Count provides a mock function with given fields: ctx, query
func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) {
ret := _m.Called(ctx, query)
var r0 int64
if rf, ok := ret.Get(0).(func(context.Context, *q.Query) int64); ok {
r0 = rf(ctx, query)
} else {
r0 = ret.Get(0).(int64)
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok {
r1 = rf(ctx, query)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Create provides a mock function with given fields: ctx, userGroup
func (_m *Manager) Create(ctx context.Context, userGroup model.UserGroup) (int, error) {
ret := _m.Called(ctx, userGroup)
var r0 int
if rf, ok := ret.Get(0).(func(context.Context, model.UserGroup) int); ok {
r0 = rf(ctx, userGroup)
} else {
r0 = ret.Get(0).(int)
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, model.UserGroup) error); ok {
r1 = rf(ctx, userGroup)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Delete provides a mock function with given fields: ctx, id
func (_m *Manager) Delete(ctx context.Context, id int) error {
ret := _m.Called(ctx, id)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, int) error); ok {
r0 = rf(ctx, id)
} else {
r0 = ret.Error(0)
}
return r0
}
// Get provides a mock function with given fields: ctx, id
func (_m *Manager) Get(ctx context.Context, id int) (*model.UserGroup, error) {
ret := _m.Called(ctx, id)
var r0 *model.UserGroup
if rf, ok := ret.Get(0).(func(context.Context, int) *model.UserGroup); ok {
r0 = rf(ctx, id)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.UserGroup)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int) error); ok {
r1 = rf(ctx, id)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// List provides a mock function with given fields: ctx, query
func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*model.UserGroup, error) {
ret := _m.Called(ctx, query)
var r0 []*model.UserGroup
if rf, ok := ret.Get(0).(func(context.Context, *q.Query) []*model.UserGroup); ok {
r0 = rf(ctx, query)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.UserGroup)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok {
r1 = rf(ctx, query)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Onboard provides a mock function with given fields: ctx, g
func (_m *Manager) Onboard(ctx context.Context, g *model.UserGroup) error {
ret := _m.Called(ctx, g)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *model.UserGroup) error); ok {
r0 = rf(ctx, g)
} else {
r0 = ret.Error(0)
}
return r0
}
// Populate provides a mock function with given fields: ctx, userGroups
func (_m *Manager) Populate(ctx context.Context, userGroups []model.UserGroup) ([]int, error) {
ret := _m.Called(ctx, userGroups)
var r0 []int
if rf, ok := ret.Get(0).(func(context.Context, []model.UserGroup) []int); ok {
r0 = rf(ctx, userGroups)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]int)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, []model.UserGroup) error); ok {
r1 = rf(ctx, userGroups)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// UpdateName provides a mock function with given fields: ctx, id, groupName
func (_m *Manager) UpdateName(ctx context.Context, id int, groupName string) error {
ret := _m.Called(ctx, id, groupName)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, int, string) error); ok {
r0 = rf(ctx, id, groupName)
} else {
r0 = ret.Error(0)
}
return r0
}
type mockConstructorTestingTNewManager interface {
mock.TestingT
Cleanup(func())
}
// NewManager creates a new instance of Manager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewManager(t mockConstructorTestingTNewManager) *Manager {
mock := &Manager{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}