Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented User-Groups integration for mcs #62

Merged
merged 1 commit into from
Apr 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions models/add_user_request.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

98 changes: 98 additions & 0 deletions models/update_user.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,7 @@ import {
Dialog,
DialogContent,
DialogTitle,
FormControl,
InputLabel,
LinearProgress,
MenuItem,
Select,
TextField
} from "@material-ui/core";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
Expand Down
8 changes: 5 additions & 3 deletions portal-ui/src/screens/Console/Users/AddUser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class AddUserContent extends React.Component<

saveRecord(event: React.FormEvent) {
event.preventDefault();
const { accessKey, addLoading, secretKey } = this.state;
const { accessKey, addLoading, secretKey, selectedGroups } = this.state;
const { selectedUser } = this.props;
if (addLoading) {
return;
Expand All @@ -96,7 +96,8 @@ class AddUserContent extends React.Component<
api
.invoke("PUT", `/api/v1/users/${selectedUser.accessKey}`, {
accessKey,
secretKey: secretKey !== "" ? null : secretKey
secretKey: secretKey !== "" ? null : secretKey,
groups: selectedGroups
})
.then(res => {
this.setState(
Expand All @@ -119,7 +120,8 @@ class AddUserContent extends React.Component<
api
.invoke("POST", "/api/v1/users", {
accessKey,
secretKey
secretKey,
groups: selectedGroups
})
.then(res => {
this.setState(
Expand Down
62 changes: 55 additions & 7 deletions restapi/admin_users.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@ func registerUsersHandlers(api *operations.McsAPI) {

return admin_api.NewUpdateUserGroupsOK().WithPayload(userUpdateResponse)
})
// Get User
api.AdminAPIGetUserInfoHandler = admin_api.GetUserInfoHandlerFunc(func(params admin_api.GetUserInfoParams, principal *models.Principal) middleware.Responder {
userInfoResponse, err := getUserInfoResponse(params)
if err != nil {
return admin_api.NewGetUserDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
}

return admin_api.NewGetUserOK().WithPayload(userInfoResponse)
})
}

func listUsers(ctx context.Context, client MinioAdmin) ([]*models.User, error) {
Expand Down Expand Up @@ -116,18 +125,28 @@ func getListUsersResponse() (*models.ListUsersResponse, error) {
}

// addUser invokes adding a users on `MinioAdmin` and builds the response `models.User`
func addUser(ctx context.Context, client MinioAdmin, accessKey, secretKey *string) (*models.User, error) {
func addUser(ctx context.Context, client MinioAdmin, accessKey, secretKey *string, groups []string) (*models.User, error) {
// Calls into MinIO to add a new user if there's an error return it
err := client.addUser(ctx, *accessKey, *secretKey)
if err != nil {
if err := client.addUser(ctx, *accessKey, *secretKey); err != nil {
return nil, err
}

userElem := &models.User{
AccessKey: *accessKey,
if len(groups) > 0 {
userElem, errUG := updateUserGroups(ctx, client, *accessKey, groups)

if errUG != nil {
return nil, errUG
}
return userElem, nil
}

return userElem, nil
userRet := &models.User{
AccessKey: *accessKey,
MemberOf: nil,
Policy: "",
Status: "",
}
return userRet, nil
}

func getUserAddResponse(params admin_api.AddUserParams) (*models.User, error) {
Expand All @@ -141,7 +160,7 @@ func getUserAddResponse(params admin_api.AddUserParams) (*models.User, error) {
// defining the client to be used
adminClient := adminClient{client: mAdmin}

user, err := addUser(ctx, adminClient, params.Body.AccessKey, params.Body.SecretKey)
user, err := addUser(ctx, adminClient, params.Body.AccessKey, params.Body.SecretKey, params.Body.Groups)
if err != nil {
log.Println("error adding user:", err)
return nil, err
Expand Down Expand Up @@ -189,6 +208,35 @@ func getUserInfo(ctx context.Context, client MinioAdmin, accessKey string) (*mad
return &userInfo, nil
}

func getUserInfoResponse(params admin_api.GetUserInfoParams) (*models.User, error) {
ctx := context.Background()

mAdmin, err := newMAdminClient()
if err != nil {
log.Println("error creating Madmin Client:", err)
return nil, err
}

// create a minioClient interface implementation
// defining the client to be used
adminClient := adminClient{client: mAdmin}

user, err := getUserInfo(ctx, adminClient, params.Name)
if err != nil {
log.Println("error getting user:", err)
return nil, err
}

userInformation := &models.User{
AccessKey: params.Name,
MemberOf: user.MemberOf,
Policy: user.PolicyName,
Status: string(user.Status),
}

return userInformation, nil
}

// updateUserGroups invokes getUserInfo() to get the old groups from the user,
// then we merge the list with the new groups list to have a shorter iteration between groups and we do a comparison between the current and old groups.
// We delete or update the groups according the location in each list and send the user with the new groups from `MinioAdmin` to the client
Expand Down
49 changes: 44 additions & 5 deletions restapi/admin_users_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ func TestListUsers(t *testing.T) {
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()"
Expand Down Expand Up @@ -113,31 +114,69 @@ func TestAddUser(t *testing.T) {
// 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
}
// adds a valid user to MinIO

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)
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-1: valid case

// 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)
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")
Expand Down
Loading