402 lines
13 KiB
Go
402 lines
13 KiB
Go
// This file is part of MinIO Console Server
|
|
// Copyright (c) 2020 MinIO, Inc.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package restapi
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/minio/minio/pkg/madmin"
|
|
|
|
"errors"
|
|
|
|
asrt "github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
// assigning mock at runtime instead of compile time
|
|
var minioListUsersMock func() (map[string]madmin.UserInfo, error)
|
|
var minioAddUserMock func(accessKey, secreyKey string) error
|
|
var minioRemoveUserMock func(accessKey string) error
|
|
var minioGetUserInfoMock func(accessKey string) (madmin.UserInfo, error)
|
|
var minioSetUserStatusMock func(accessKey string, status madmin.AccountStatus) error
|
|
|
|
// mock function of listUsers()
|
|
func (ac adminClientMock) listUsers(ctx context.Context) (map[string]madmin.UserInfo, error) {
|
|
return minioListUsersMock()
|
|
}
|
|
|
|
// mock function of addUser()
|
|
func (ac adminClientMock) addUser(ctx context.Context, accessKey, secretKey string) error {
|
|
return minioAddUserMock(accessKey, secretKey)
|
|
}
|
|
|
|
// mock function of removeUser()
|
|
func (ac adminClientMock) removeUser(ctx context.Context, accessKey string) error {
|
|
return minioRemoveUserMock(accessKey)
|
|
}
|
|
|
|
//mock function of getUserInfo()
|
|
func (ac adminClientMock) getUserInfo(ctx context.Context, accessKey string) (madmin.UserInfo, error) {
|
|
return minioGetUserInfoMock(accessKey)
|
|
}
|
|
|
|
//mock function of setUserStatus()
|
|
func (ac adminClientMock) setUserStatus(ctx context.Context, accessKey string, status madmin.AccountStatus) error {
|
|
return minioSetUserStatusMock(accessKey, status)
|
|
}
|
|
|
|
func TestListUsers(t *testing.T) {
|
|
assert := asrt.New(t)
|
|
adminClient := adminClientMock{}
|
|
ctx := context.Background()
|
|
// Test-1 : listUsers() Get response from minio client with two users and return the same number on listUsers()
|
|
// mock minIO client
|
|
mockUserMap := map[string]madmin.UserInfo{
|
|
"ABCDEFGHI": {
|
|
SecretKey: "",
|
|
PolicyName: "ABCDEFGHI-policy",
|
|
Status: "enabled",
|
|
MemberOf: []string{"group1", "group2"},
|
|
},
|
|
"ZBCDEFGHI": {
|
|
SecretKey: "",
|
|
PolicyName: "ZBCDEFGHI-policy",
|
|
Status: "enabled",
|
|
MemberOf: []string{"group1", "group2"},
|
|
},
|
|
}
|
|
|
|
// mock function response from listUsersWithContext(ctx)
|
|
minioListUsersMock = func() (map[string]madmin.UserInfo, error) {
|
|
return mockUserMap, nil
|
|
}
|
|
|
|
// get list users response this response should have Name, CreationDate, Size and Access
|
|
// as part of of each user
|
|
function := "listUsers()"
|
|
userMap, err := listUsers(ctx, adminClient)
|
|
if err != nil {
|
|
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
|
}
|
|
// verify length of users is correct
|
|
assert.Equal(len(mockUserMap), len(userMap), fmt.Sprintf("Failed on %s: length of user's lists is not the same", function))
|
|
|
|
for _, b := range userMap {
|
|
assert.Contains(mockUserMap, b.AccessKey)
|
|
assert.Equal(string(mockUserMap[b.AccessKey].Status), b.Status)
|
|
assert.Equal(mockUserMap[b.AccessKey].PolicyName, b.Policy)
|
|
assert.ElementsMatch(mockUserMap[b.AccessKey].MemberOf, []string{"group1", "group2"})
|
|
}
|
|
|
|
// Test-2 : listUsers() Return and see that the error is handled correctly and returned
|
|
minioListUsersMock = func() (map[string]madmin.UserInfo, error) {
|
|
return nil, errors.New("error")
|
|
}
|
|
_, err = listUsers(ctx, adminClient)
|
|
if assert.Error(err) {
|
|
assert.Equal("error", err.Error())
|
|
}
|
|
}
|
|
|
|
func TestAddUser(t *testing.T) {
|
|
assert := asrt.New(t)
|
|
adminClient := adminClientMock{}
|
|
ctx := context.Background()
|
|
// Test-1: valid case of adding a user with a proper access key
|
|
accessKey := "ABCDEFGHI"
|
|
secretKey := "ABCDEFGHIABCDEFGHI"
|
|
groups := []string{"group1", "group2", "group3"}
|
|
emptyGroupTest := []string{}
|
|
mockResponse := &madmin.UserInfo{
|
|
MemberOf: []string{"group1", "group2", "gropup3"},
|
|
PolicyName: "",
|
|
Status: "enabled",
|
|
SecretKey: "",
|
|
}
|
|
|
|
// mock function response from addUser() return no error
|
|
minioAddUserMock = func(accessKey, secretKey string) error {
|
|
return nil
|
|
}
|
|
|
|
minioGetUserInfoMock = func(accessKey string) (madmin.UserInfo, error) {
|
|
return *mockResponse, nil
|
|
}
|
|
|
|
minioUpdateGroupMembersMock = func(remove madmin.GroupAddRemove) error {
|
|
return nil
|
|
}
|
|
// Test-1: Add a user
|
|
function := "addUser()"
|
|
user, err := addUser(ctx, adminClient, &accessKey, &secretKey, groups)
|
|
if err != nil {
|
|
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
|
}
|
|
// no error should have been returned
|
|
assert.Nil(err, "Error is not null")
|
|
// the same access key should be in the model users
|
|
assert.Equal(user.AccessKey, accessKey)
|
|
|
|
// Test-2 Add a user with empty groups list
|
|
user, err = addUser(ctx, adminClient, &accessKey, &secretKey, emptyGroupTest)
|
|
// no error should have been returned
|
|
assert.Nil(err, "Error is not null")
|
|
// the same access key should be in the model users
|
|
assert.Equal(user.AccessKey, accessKey)
|
|
|
|
// Test-3: valid case
|
|
accessKey = "AB"
|
|
secretKey = "ABCDEFGHIABCDEFGHI"
|
|
// mock function response from addUser() return no error
|
|
minioAddUserMock = func(accessKey, secretKey string) error {
|
|
return errors.New("error")
|
|
}
|
|
|
|
user, err = addUser(ctx, adminClient, &accessKey, &secretKey, groups)
|
|
|
|
// no error should have been returned
|
|
assert.Nil(user, "User is not null")
|
|
assert.NotNil(err, "An error should have been returned")
|
|
|
|
if assert.Error(err) {
|
|
assert.Equal("error", err.Error())
|
|
}
|
|
|
|
// Test-4: add groups function returns an error
|
|
minioUpdateGroupMembersMock = func(remove madmin.GroupAddRemove) error {
|
|
return errors.New("error")
|
|
}
|
|
|
|
user, err = addUser(ctx, adminClient, &accessKey, &secretKey, groups)
|
|
|
|
// no error should have been returned
|
|
assert.Nil(user, "User is not null")
|
|
assert.NotNil(err, "An error should have been returned")
|
|
|
|
if assert.Error(err) {
|
|
assert.Equal("error", err.Error())
|
|
}
|
|
}
|
|
|
|
func TestRemoveUser(t *testing.T) {
|
|
assert := asrt.New(t)
|
|
// mock minIO client
|
|
adminClient := adminClientMock{}
|
|
ctx := context.Background()
|
|
function := "removeUser()"
|
|
|
|
// Test-1: removeUser() delete a user
|
|
// mock function response from removeUser(accessKey)
|
|
minioRemoveUserMock = func(accessKey string) error {
|
|
return nil
|
|
}
|
|
|
|
if err := removeUser(ctx, adminClient, "ABCDEFGHI"); err != nil {
|
|
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
|
}
|
|
|
|
// Test-2: removeUser() make sure errors are handled correctly when error on DeleteUser()
|
|
// mock function response from removeUser(accessKey)
|
|
minioRemoveUserMock = func(accessKey string) error {
|
|
return errors.New("error")
|
|
}
|
|
|
|
if err := removeUser(ctx, adminClient, "notexistentuser"); assert.Error(err) {
|
|
assert.Equal("error", err.Error())
|
|
}
|
|
}
|
|
|
|
func TestUserGroups(t *testing.T) {
|
|
assert := asrt.New(t)
|
|
// mock minIO client
|
|
adminClient := adminClientMock{}
|
|
ctx := context.Background()
|
|
|
|
function := "updateUserGroups()"
|
|
mockUserGroups := []string{"group1", "group2", "group3"}
|
|
mockUserName := "testUser"
|
|
mockResponse := &madmin.UserInfo{
|
|
MemberOf: []string{"group1", "group2", "gropup3"},
|
|
PolicyName: "",
|
|
Status: "enabled",
|
|
SecretKey: mockUserName,
|
|
}
|
|
mockEmptyResponse := &madmin.UserInfo{
|
|
MemberOf: nil,
|
|
PolicyName: "",
|
|
Status: "",
|
|
SecretKey: "",
|
|
}
|
|
|
|
// Test-1: updateUserGroups() updates the groups for a user
|
|
// mock function response from updateUserGroups(accessKey, groupsToAssign)
|
|
|
|
minioGetUserInfoMock = func(accessKey string) (madmin.UserInfo, error) {
|
|
return *mockResponse, nil
|
|
}
|
|
|
|
minioUpdateGroupMembersMock = func(remove madmin.GroupAddRemove) error {
|
|
return nil
|
|
}
|
|
|
|
if _, err := updateUserGroups(ctx, adminClient, mockUserName, mockUserGroups); err != nil {
|
|
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
|
}
|
|
|
|
// Test-2: updateUserGroups() make sure errors are handled correctly when error on UpdateGroupMembersMock()
|
|
// mock function response from removeUser(accessKey)
|
|
|
|
minioUpdateGroupMembersMock = func(remove madmin.GroupAddRemove) error {
|
|
return errors.New("error")
|
|
}
|
|
|
|
if _, err := updateUserGroups(ctx, adminClient, mockUserName, mockUserGroups); assert.Error(err) {
|
|
assert.Equal("there was an error updating the groups", err.Error())
|
|
}
|
|
|
|
// Test-3: updateUserGroups() make sure we return the correct error when getUserInfo returns error
|
|
minioGetUserInfoMock = func(accessKey string) (madmin.UserInfo, error) {
|
|
return *mockEmptyResponse, errors.New("error getting user ")
|
|
}
|
|
|
|
minioUpdateGroupMembersMock = func(remove madmin.GroupAddRemove) error {
|
|
return nil
|
|
}
|
|
|
|
if _, err := updateUserGroups(ctx, adminClient, mockUserName, mockUserGroups); assert.Error(err) {
|
|
assert.Equal("error getting user ", err.Error())
|
|
}
|
|
}
|
|
|
|
func TestGetUserInfo(t *testing.T) {
|
|
assert := asrt.New(t)
|
|
adminClient := adminClientMock{}
|
|
ctx := context.Background()
|
|
|
|
// Test-1 : getUserInfo() get user info
|
|
userName := "userNameTest"
|
|
mockResponse := &madmin.UserInfo{
|
|
SecretKey: userName,
|
|
PolicyName: "",
|
|
MemberOf: []string{"group1", "group2", "group3"},
|
|
Status: "enabled",
|
|
}
|
|
emptyMockResponse := &madmin.UserInfo{
|
|
SecretKey: "",
|
|
PolicyName: "",
|
|
Status: "",
|
|
MemberOf: nil,
|
|
}
|
|
|
|
// mock function response from getUserInfo()
|
|
minioGetUserInfoMock = func(username string) (madmin.UserInfo, error) {
|
|
return *mockResponse, nil
|
|
}
|
|
function := "getUserInfo()"
|
|
info, err := getUserInfo(ctx, adminClient, userName)
|
|
if err != nil {
|
|
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
|
}
|
|
|
|
assert.Equal(userName, info.SecretKey)
|
|
assert.Equal("", info.PolicyName)
|
|
assert.ElementsMatch([]string{"group1", "group2", "group3"}, info.MemberOf)
|
|
assert.Equal(mockResponse.Status, info.Status)
|
|
|
|
// Test-2 : getUserInfo() Return error and see that the error is handled correctly and returned
|
|
minioGetUserInfoMock = func(username string) (madmin.UserInfo, error) {
|
|
return *emptyMockResponse, errors.New("error")
|
|
}
|
|
_, err = getUserInfo(ctx, adminClient, userName)
|
|
if assert.Error(err) {
|
|
assert.Equal("error", err.Error())
|
|
}
|
|
}
|
|
|
|
func TestSetUserStatus(t *testing.T) {
|
|
assert := asrt.New(t)
|
|
adminClient := adminClientMock{}
|
|
function := "setUserStatus()"
|
|
userName := "userName123"
|
|
ctx := context.Background()
|
|
|
|
// Test-1: setUserStatus() update valid disabled status
|
|
expectedStatus := "disabled"
|
|
minioSetUserStatusMock = func(accessKey string, status madmin.AccountStatus) error {
|
|
return nil
|
|
}
|
|
if err := setUserStatus(ctx, adminClient, userName, expectedStatus); err != nil {
|
|
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
|
}
|
|
// Test-2: setUserStatus() update valid enabled status
|
|
expectedStatus = "enabled"
|
|
minioSetUserStatusMock = func(accessKey string, status madmin.AccountStatus) error {
|
|
return nil
|
|
}
|
|
if err := setUserStatus(ctx, adminClient, userName, expectedStatus); err != nil {
|
|
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
|
}
|
|
// Test-3: setUserStatus() update invalid status, should send error
|
|
expectedStatus = "invalid"
|
|
minioSetUserStatusMock = func(accessKey string, status madmin.AccountStatus) error {
|
|
return nil
|
|
}
|
|
if err := setUserStatus(ctx, adminClient, userName, expectedStatus); assert.Error(err) {
|
|
assert.Equal("status not valid", err.Error())
|
|
}
|
|
// Test-4: setUserStatus() handler error correctly
|
|
expectedStatus = "enabled"
|
|
minioSetUserStatusMock = func(accessKey string, status madmin.AccountStatus) error {
|
|
return errors.New("error")
|
|
}
|
|
if err := setUserStatus(ctx, adminClient, userName, expectedStatus); assert.Error(err) {
|
|
assert.Equal("error", err.Error())
|
|
}
|
|
}
|
|
|
|
func TestUserGroupsBulk(t *testing.T) {
|
|
assert := asrt.New(t)
|
|
// mock minIO client
|
|
adminClient := adminClientMock{}
|
|
ctx := context.Background()
|
|
|
|
function := "updateUserGroups()"
|
|
mockUserGroups := []string{"group1", "group2", "group3"}
|
|
mockUsers := []string{"testUser", "testUser2"}
|
|
|
|
// Test-1: addUsersListToGroups() updates the groups for a users list
|
|
// mock function response from updateUserGroups(accessKey, groupsToAssign)
|
|
minioUpdateGroupMembersMock = func(remove madmin.GroupAddRemove) error {
|
|
return nil
|
|
}
|
|
|
|
if err := addUsersListToGroups(ctx, adminClient, mockUsers, mockUserGroups); err != nil {
|
|
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
|
}
|
|
|
|
// Test-2: addUsersListToGroups() make sure errors are handled correctly when error on updateGroupMembers()
|
|
// mock function response from removeUser(accessKey)
|
|
minioUpdateGroupMembersMock = func(remove madmin.GroupAddRemove) error {
|
|
return errors.New("error")
|
|
}
|
|
|
|
if err := addUsersListToGroups(ctx, adminClient, mockUsers, mockUserGroups); assert.Error(err) {
|
|
assert.Equal("error in users-groups assignation: \"error,error,error\"", err.Error())
|
|
}
|
|
}
|