prepareError receives an array of errors and return *model.Error object with a message and error code, we can extend this function to add more error types/code
335 lines
11 KiB
Go
335 lines
11 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"
|
|
"log"
|
|
|
|
"github.com/go-openapi/errors"
|
|
"github.com/go-openapi/runtime/middleware"
|
|
"github.com/minio/console/restapi/operations"
|
|
"github.com/minio/minio/pkg/madmin"
|
|
|
|
"github.com/minio/console/restapi/operations/admin_api"
|
|
|
|
"github.com/minio/console/models"
|
|
)
|
|
|
|
func registerGroupsHandlers(api *operations.ConsoleAPI) {
|
|
// List Groups
|
|
api.AdminAPIListGroupsHandler = admin_api.ListGroupsHandlerFunc(func(params admin_api.ListGroupsParams, session *models.Principal) middleware.Responder {
|
|
listGroupsResponse, err := getListGroupsResponse(session)
|
|
if err != nil {
|
|
return admin_api.NewListGroupsDefault(int(err.Code)).WithPayload(err)
|
|
}
|
|
return admin_api.NewListGroupsOK().WithPayload(listGroupsResponse)
|
|
})
|
|
// Group Info
|
|
api.AdminAPIGroupInfoHandler = admin_api.GroupInfoHandlerFunc(func(params admin_api.GroupInfoParams, session *models.Principal) middleware.Responder {
|
|
groupInfo, err := getGroupInfoResponse(session, params)
|
|
if err != nil {
|
|
return admin_api.NewGroupInfoDefault(int(err.Code)).WithPayload(err)
|
|
}
|
|
return admin_api.NewGroupInfoOK().WithPayload(groupInfo)
|
|
})
|
|
// Add Group
|
|
api.AdminAPIAddGroupHandler = admin_api.AddGroupHandlerFunc(func(params admin_api.AddGroupParams, session *models.Principal) middleware.Responder {
|
|
if err := getAddGroupResponse(session, params.Body); err != nil {
|
|
return admin_api.NewAddGroupDefault(int(err.Code)).WithPayload(err)
|
|
}
|
|
return admin_api.NewAddGroupCreated()
|
|
})
|
|
// Remove Group
|
|
api.AdminAPIRemoveGroupHandler = admin_api.RemoveGroupHandlerFunc(func(params admin_api.RemoveGroupParams, session *models.Principal) middleware.Responder {
|
|
if err := getRemoveGroupResponse(session, params); err != nil {
|
|
return admin_api.NewRemoveGroupDefault(int(err.Code)).WithPayload(err)
|
|
}
|
|
return admin_api.NewRemoveGroupNoContent()
|
|
})
|
|
// Update Group
|
|
api.AdminAPIUpdateGroupHandler = admin_api.UpdateGroupHandlerFunc(func(params admin_api.UpdateGroupParams, session *models.Principal) middleware.Responder {
|
|
groupUpdateResp, err := getUpdateGroupResponse(session, params)
|
|
if err != nil {
|
|
return admin_api.NewUpdateGroupDefault(int(err.Code)).WithPayload(err)
|
|
}
|
|
return admin_api.NewUpdateGroupOK().WithPayload(groupUpdateResp)
|
|
})
|
|
}
|
|
|
|
// listGroups calls MinIO server to list all groups names present on the server.
|
|
func listGroups(ctx context.Context, client MinioAdmin) (*[]string, error) {
|
|
groupList, err := client.listGroups(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &groupList, nil
|
|
}
|
|
|
|
// getListGroupsResponse performs listGroups() and serializes it to the handler's output
|
|
func getListGroupsResponse(session *models.Principal) (*models.ListGroupsResponse, *models.Error) {
|
|
ctx := context.Background()
|
|
mAdmin, err := newMAdminClient(session)
|
|
if err != nil {
|
|
return nil, prepareError(err)
|
|
}
|
|
// create a MinIO Admin Client interface implementation
|
|
// defining the client to be used
|
|
adminClient := adminClient{client: mAdmin}
|
|
|
|
groups, err := listGroups(ctx, adminClient)
|
|
if err != nil {
|
|
return nil, prepareError(err)
|
|
}
|
|
// serialize output
|
|
listGroupsResponse := &models.ListGroupsResponse{
|
|
Groups: *groups,
|
|
Total: int64(len(*groups)),
|
|
}
|
|
return listGroupsResponse, nil
|
|
}
|
|
|
|
// groupInfo calls MinIO server get Group's info
|
|
func groupInfo(ctx context.Context, client MinioAdmin, group string) (*madmin.GroupDesc, error) {
|
|
groupDesc, err := client.getGroupDescription(ctx, group)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return groupDesc, nil
|
|
}
|
|
|
|
// getGroupInfoResponse performs groupInfo() and serializes it to the handler's output
|
|
func getGroupInfoResponse(session *models.Principal, params admin_api.GroupInfoParams) (*models.Group, *models.Error) {
|
|
ctx := context.Background()
|
|
mAdmin, err := newMAdminClient(session)
|
|
if err != nil {
|
|
return nil, prepareError(err)
|
|
}
|
|
// create a MinIO Admin Client interface implementation
|
|
// defining the client to be used
|
|
adminClient := adminClient{client: mAdmin}
|
|
|
|
groupDesc, err := groupInfo(ctx, adminClient, params.Name)
|
|
if err != nil {
|
|
return nil, prepareError(err)
|
|
}
|
|
|
|
groupResponse := &models.Group{
|
|
Members: groupDesc.Members,
|
|
Name: groupDesc.Name,
|
|
Policy: groupDesc.Policy,
|
|
Status: groupDesc.Status}
|
|
|
|
return groupResponse, nil
|
|
}
|
|
|
|
// addGroupAdd a MinIO group with the defined members
|
|
func addGroup(ctx context.Context, client MinioAdmin, group string, members []string) error {
|
|
gAddRemove := madmin.GroupAddRemove{
|
|
Group: group,
|
|
Members: members,
|
|
IsRemove: false,
|
|
}
|
|
err := client.updateGroupMembers(ctx, gAddRemove)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// getAddGroupResponse performs addGroup() and serializes it to the handler's output
|
|
func getAddGroupResponse(session *models.Principal, params *models.AddGroupRequest) *models.Error {
|
|
ctx := context.Background()
|
|
// AddGroup request needed to proceed
|
|
if params == nil {
|
|
return prepareError(errGroupBodyNotInRequest)
|
|
}
|
|
|
|
mAdmin, err := newMAdminClient(session)
|
|
if err != nil {
|
|
return prepareError(err)
|
|
}
|
|
// create a MinIO Admin Client interface implementation
|
|
// defining the client to be used
|
|
adminClient := adminClient{client: mAdmin}
|
|
|
|
if err := addGroup(ctx, adminClient, *params.Group, params.Members); err != nil {
|
|
return prepareError(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// removeGroup deletes a minIO group only if it has no members
|
|
func removeGroup(ctx context.Context, client MinioAdmin, group string) error {
|
|
gAddRemove := madmin.GroupAddRemove{
|
|
Group: group,
|
|
Members: []string{},
|
|
IsRemove: true,
|
|
}
|
|
err := client.updateGroupMembers(ctx, gAddRemove)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// getRemoveGroupResponse performs removeGroup() and serializes it to the handler's output
|
|
func getRemoveGroupResponse(session *models.Principal, params admin_api.RemoveGroupParams) *models.Error {
|
|
ctx := context.Background()
|
|
|
|
if params.Name == "" {
|
|
return prepareError(errGroupNameNotInRequest)
|
|
}
|
|
mAdmin, err := newMAdminClient(session)
|
|
if err != nil {
|
|
return prepareError(err)
|
|
}
|
|
// create a MinIO Admin Client interface implementation
|
|
// defining the client to be used
|
|
adminClient := adminClient{client: mAdmin}
|
|
|
|
if err := removeGroup(ctx, adminClient, params.Name); err != nil {
|
|
return prepareError(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// updateGroup updates a group by adding/removing members and setting the status to the desired one
|
|
//
|
|
// isRemove: whether remove members or not
|
|
func updateGroupMembers(ctx context.Context, client MinioAdmin, group string, members []string, isRemove bool) error {
|
|
gAddRemove := madmin.GroupAddRemove{
|
|
Group: group,
|
|
Members: members,
|
|
IsRemove: isRemove,
|
|
}
|
|
err := client.updateGroupMembers(ctx, gAddRemove)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// addOrDeleteMembers updates a group members by adding or deleting them based on the expectedMembers
|
|
func addOrDeleteMembers(ctx context.Context, client MinioAdmin, group *madmin.GroupDesc, expectedMembers []string) error {
|
|
// get members to delete/add
|
|
membersToDelete := DifferenceArrays(group.Members, expectedMembers)
|
|
membersToAdd := DifferenceArrays(expectedMembers, group.Members)
|
|
// delete members if any to be deleted
|
|
if len(membersToDelete) > 0 {
|
|
err := updateGroupMembers(ctx, client, group.Name, membersToDelete, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
// add members if any to be added
|
|
if len(membersToAdd) > 0 {
|
|
err := updateGroupMembers(ctx, client, group.Name, membersToAdd, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func setGroupStatus(ctx context.Context, client MinioAdmin, group, status string) error {
|
|
var setStatus madmin.GroupStatus
|
|
switch status {
|
|
case "enabled":
|
|
setStatus = madmin.GroupEnabled
|
|
case "disabled":
|
|
setStatus = madmin.GroupDisabled
|
|
default:
|
|
return errors.New(500, "status not valid")
|
|
}
|
|
if err := client.setGroupStatus(ctx, group, setStatus); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// getUpdateGroupResponse updates a group by adding or removing it's members depending on the request,
|
|
// also sets the group's status if status in the request is different than the current one.
|
|
// Then serializes the output to be used by the handler.
|
|
func getUpdateGroupResponse(session *models.Principal, params admin_api.UpdateGroupParams) (*models.Group, *models.Error) {
|
|
ctx := context.Background()
|
|
if params.Name == "" {
|
|
return nil, prepareError(errGroupNameNotInRequest)
|
|
}
|
|
if params.Body == nil {
|
|
return nil, prepareError(errGroupBodyNotInRequest)
|
|
|
|
}
|
|
expectedGroupUpdate := params.Body
|
|
groupName := params.Name
|
|
|
|
mAdmin, err := newMAdminClient(session)
|
|
if err != nil {
|
|
return nil, prepareError(err)
|
|
}
|
|
// create a MinIO Admin Client interface implementation
|
|
// defining the client to be used
|
|
adminClient := adminClient{client: mAdmin}
|
|
|
|
groupUpdated, err := groupUpdate(ctx, adminClient, groupName, expectedGroupUpdate)
|
|
if err != nil {
|
|
return nil, prepareError(err)
|
|
}
|
|
groupResponse := &models.Group{
|
|
Name: groupUpdated.Name,
|
|
Members: groupUpdated.Members,
|
|
Policy: groupUpdated.Policy,
|
|
Status: groupUpdated.Status,
|
|
}
|
|
return groupResponse, nil
|
|
}
|
|
|
|
// groupUpdate updates a group given the expected parameters, compares the expected parameters against the current ones
|
|
// and updates them accordingly, status is only updated if the expected status is different than the current one.
|
|
// Then fetches the group again to return the object updated.
|
|
func groupUpdate(ctx context.Context, client MinioAdmin, groupName string, expectedGroup *models.UpdateGroupRequest) (*madmin.GroupDesc, error) {
|
|
expectedMembers := expectedGroup.Members
|
|
expectedStatus := *expectedGroup.Status
|
|
// get current members and status
|
|
groupDescription, err := groupInfo(ctx, client, groupName)
|
|
if err != nil {
|
|
log.Println("error getting group info:", err)
|
|
return nil, err
|
|
}
|
|
// update group members
|
|
err = addOrDeleteMembers(ctx, client, groupDescription, expectedMembers)
|
|
if err != nil {
|
|
log.Println("error updating group:", err)
|
|
return nil, err
|
|
}
|
|
// update group status only if different from current status
|
|
if expectedStatus != groupDescription.Status {
|
|
err = setGroupStatus(ctx, client, groupDescription.Name, expectedStatus)
|
|
if err != nil {
|
|
log.Println("error updating group's status:", err)
|
|
return nil, err
|
|
}
|
|
}
|
|
// return latest group info to verify that changes were applied correctly
|
|
groupDescription, err = groupInfo(ctx, client, groupName)
|
|
if err != nil {
|
|
log.Println("error getting group info:", err)
|
|
return nil, err
|
|
}
|
|
return groupDescription, nil
|
|
}
|