github.com/minio/console@v1.4.1/api/admin_users.go (about)

     1  // This file is part of MinIO Console Server
     2  // Copyright (c) 2021 MinIO, Inc.
     3  //
     4  // This program is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Affero General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // This program is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  // GNU Affero General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Affero General Public License
    15  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package api
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"sort"
    23  	"strings"
    24  
    25  	"github.com/minio/console/pkg/utils"
    26  
    27  	"github.com/go-openapi/errors"
    28  	"github.com/go-openapi/runtime/middleware"
    29  	"github.com/minio/console/api/operations"
    30  	accountApi "github.com/minio/console/api/operations/account"
    31  	bucketApi "github.com/minio/console/api/operations/bucket"
    32  	userApi "github.com/minio/console/api/operations/user"
    33  	"github.com/minio/console/models"
    34  	"github.com/minio/madmin-go/v3"
    35  	iampolicy "github.com/minio/pkg/v3/policy"
    36  )
    37  
    38  // Policy evaluated constants
    39  const (
    40  	Unknown = 0
    41  	Allow   = 1
    42  	Deny    = -1
    43  )
    44  
    45  func registerUsersHandlers(api *operations.ConsoleAPI) {
    46  	// List Users
    47  	api.UserListUsersHandler = userApi.ListUsersHandlerFunc(func(params userApi.ListUsersParams, session *models.Principal) middleware.Responder {
    48  		listUsersResponse, err := getListUsersResponse(session, params)
    49  		if err != nil {
    50  			return userApi.NewListUsersDefault(err.Code).WithPayload(err.APIError)
    51  		}
    52  		return userApi.NewListUsersOK().WithPayload(listUsersResponse)
    53  	})
    54  	// Add User
    55  	api.UserAddUserHandler = userApi.AddUserHandlerFunc(func(params userApi.AddUserParams, session *models.Principal) middleware.Responder {
    56  		userResponse, err := getUserAddResponse(session, params)
    57  		if err != nil {
    58  			return userApi.NewAddUserDefault(err.Code).WithPayload(err.APIError)
    59  		}
    60  		return userApi.NewAddUserCreated().WithPayload(userResponse)
    61  	})
    62  	// Remove User
    63  	api.UserRemoveUserHandler = userApi.RemoveUserHandlerFunc(func(params userApi.RemoveUserParams, session *models.Principal) middleware.Responder {
    64  		err := getRemoveUserResponse(session, params)
    65  		if err != nil {
    66  			return userApi.NewRemoveUserDefault(err.Code).WithPayload(err.APIError)
    67  		}
    68  		return userApi.NewRemoveUserNoContent()
    69  	})
    70  	// Update User-Groups
    71  	api.UserUpdateUserGroupsHandler = userApi.UpdateUserGroupsHandlerFunc(func(params userApi.UpdateUserGroupsParams, session *models.Principal) middleware.Responder {
    72  		userUpdateResponse, err := getUpdateUserGroupsResponse(session, params)
    73  		if err != nil {
    74  			return userApi.NewUpdateUserGroupsDefault(err.Code).WithPayload(err.APIError)
    75  		}
    76  
    77  		return userApi.NewUpdateUserGroupsOK().WithPayload(userUpdateResponse)
    78  	})
    79  	// Get User
    80  	api.UserGetUserInfoHandler = userApi.GetUserInfoHandlerFunc(func(params userApi.GetUserInfoParams, session *models.Principal) middleware.Responder {
    81  		userInfoResponse, err := getUserInfoResponse(session, params)
    82  		if err != nil {
    83  			return userApi.NewGetUserInfoDefault(err.Code).WithPayload(err.APIError)
    84  		}
    85  
    86  		return userApi.NewGetUserInfoOK().WithPayload(userInfoResponse)
    87  	})
    88  	// Update User
    89  	api.UserUpdateUserInfoHandler = userApi.UpdateUserInfoHandlerFunc(func(params userApi.UpdateUserInfoParams, session *models.Principal) middleware.Responder {
    90  		userUpdateResponse, err := getUpdateUserResponse(session, params)
    91  		if err != nil {
    92  			return userApi.NewUpdateUserInfoDefault(err.Code).WithPayload(err.APIError)
    93  		}
    94  
    95  		return userApi.NewUpdateUserInfoOK().WithPayload(userUpdateResponse)
    96  	})
    97  	// Update User-Groups Bulk
    98  	api.UserBulkUpdateUsersGroupsHandler = userApi.BulkUpdateUsersGroupsHandlerFunc(func(params userApi.BulkUpdateUsersGroupsParams, session *models.Principal) middleware.Responder {
    99  		err := getAddUsersListToGroupsResponse(session, params)
   100  		if err != nil {
   101  			return userApi.NewBulkUpdateUsersGroupsDefault(err.Code).WithPayload(err.APIError)
   102  		}
   103  
   104  		return userApi.NewBulkUpdateUsersGroupsOK()
   105  	})
   106  	api.BucketListUsersWithAccessToBucketHandler = bucketApi.ListUsersWithAccessToBucketHandlerFunc(func(params bucketApi.ListUsersWithAccessToBucketParams, session *models.Principal) middleware.Responder {
   107  		response, err := getListUsersWithAccessToBucketResponse(session, params)
   108  		if err != nil {
   109  			return bucketApi.NewListUsersWithAccessToBucketDefault(err.Code).WithPayload(err.APIError)
   110  		}
   111  		return bucketApi.NewListUsersWithAccessToBucketOK().WithPayload(response)
   112  	})
   113  	// Change User Password
   114  	api.AccountChangeUserPasswordHandler = accountApi.ChangeUserPasswordHandlerFunc(func(params accountApi.ChangeUserPasswordParams, session *models.Principal) middleware.Responder {
   115  		err := getChangeUserPasswordResponse(session, params)
   116  		if err != nil {
   117  			return accountApi.NewChangeUserPasswordDefault(err.Code).WithPayload(err.APIError)
   118  		}
   119  		return accountApi.NewChangeUserPasswordCreated()
   120  	})
   121  	// Check number of Service Accounts for listed users
   122  	api.UserCheckUserServiceAccountsHandler = userApi.CheckUserServiceAccountsHandlerFunc(func(params userApi.CheckUserServiceAccountsParams, session *models.Principal) middleware.Responder {
   123  		userSAList, err := getCheckUserSAResponse(session, params)
   124  		if err != nil {
   125  			return userApi.NewCheckUserServiceAccountsDefault(err.Code).WithPayload(err.APIError)
   126  		}
   127  		return userApi.NewCheckUserServiceAccountsOK().WithPayload(userSAList)
   128  	})
   129  }
   130  
   131  func listUsers(ctx context.Context, client MinioAdmin) ([]*models.User, error) {
   132  	// Get list of all users in the MinIO
   133  	// This call requires explicit authentication, no anonymous requests are
   134  	// allowed for listing users.
   135  	userMap, err := client.listUsers(ctx)
   136  	if err != nil {
   137  		return []*models.User{}, err
   138  	}
   139  
   140  	var users []*models.User
   141  	for accessKey, user := range userMap {
   142  		userElem := &models.User{
   143  			AccessKey: accessKey,
   144  			Status:    string(user.Status),
   145  			Policy:    strings.Split(user.PolicyName, ","),
   146  			MemberOf:  user.MemberOf,
   147  		}
   148  		users = append(users, userElem)
   149  	}
   150  
   151  	return users, nil
   152  }
   153  
   154  // getListUsersResponse performs listUsers() and serializes it to the handler's output
   155  func getListUsersResponse(session *models.Principal, params userApi.ListUsersParams) (*models.ListUsersResponse, *CodedAPIError) {
   156  	ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
   157  	defer cancel()
   158  	mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
   159  	if err != nil {
   160  		return nil, ErrorWithContext(ctx, err)
   161  	}
   162  	// create a minioClient interface implementation
   163  	// defining the client to be used
   164  	adminClient := AdminClient{Client: mAdmin}
   165  	users, err := listUsers(ctx, adminClient)
   166  	if err != nil {
   167  		return nil, ErrorWithContext(ctx, err)
   168  	}
   169  	// serialize output
   170  	listUsersResponse := &models.ListUsersResponse{
   171  		Users: users,
   172  	}
   173  	return listUsersResponse, nil
   174  }
   175  
   176  // addUser invokes adding a users on `MinioAdmin` and builds the response `models.User`
   177  func addUser(ctx context.Context, client MinioAdmin, accessKey, secretKey *string, groups []string, policies []string) (*models.User, error) {
   178  	// Calls into MinIO to add a new user if there's an errors return it
   179  	if err := client.addUser(ctx, *accessKey, *secretKey); err != nil {
   180  		return nil, err
   181  	}
   182  	// set groups for the newly created user
   183  	var userWithGroups *models.User
   184  	if len(groups) > 0 {
   185  		var errUG error
   186  		userWithGroups, errUG = updateUserGroups(ctx, client, *accessKey, groups)
   187  
   188  		if errUG != nil {
   189  			return nil, errUG
   190  		}
   191  	}
   192  	// set policies for the newly created user
   193  	if len(policies) > 0 {
   194  		policyString := strings.Join(policies, ",")
   195  		if err := SetPolicy(ctx, client, policyString, *accessKey, "user"); err != nil {
   196  			return nil, err
   197  		}
   198  	}
   199  
   200  	memberOf := []string{}
   201  	status := "enabled"
   202  	if userWithGroups != nil {
   203  		memberOf = userWithGroups.MemberOf
   204  		status = userWithGroups.Status
   205  	}
   206  
   207  	userRet := &models.User{
   208  		AccessKey: *accessKey,
   209  		MemberOf:  memberOf,
   210  		Policy:    policies,
   211  		Status:    status,
   212  	}
   213  	return userRet, nil
   214  }
   215  
   216  func getUserAddResponse(session *models.Principal, params userApi.AddUserParams) (*models.User, *CodedAPIError) {
   217  	ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
   218  	defer cancel()
   219  	mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
   220  	if err != nil {
   221  		return nil, ErrorWithContext(ctx, err)
   222  	}
   223  	// create a minioClient interface implementation
   224  	// defining the client to be used
   225  	adminClient := AdminClient{Client: mAdmin}
   226  	var userExists bool
   227  
   228  	_, err = adminClient.getUserInfo(ctx, *params.Body.AccessKey)
   229  	userExists = err == nil
   230  
   231  	if userExists {
   232  		return nil, ErrorWithContext(ctx, ErrNonUniqueAccessKey)
   233  	}
   234  	user, err := addUser(
   235  		ctx,
   236  		adminClient,
   237  		params.Body.AccessKey,
   238  		params.Body.SecretKey,
   239  		params.Body.Groups,
   240  		params.Body.Policies,
   241  	)
   242  	if err != nil {
   243  		return nil, ErrorWithContext(ctx, err)
   244  	}
   245  	return user, nil
   246  }
   247  
   248  // removeUser invokes removing an user on `MinioAdmin`, then we return the response from API
   249  func removeUser(ctx context.Context, client MinioAdmin, accessKey string) error {
   250  	return client.removeUser(ctx, accessKey)
   251  }
   252  
   253  func getRemoveUserResponse(session *models.Principal, params userApi.RemoveUserParams) *CodedAPIError {
   254  	ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
   255  	defer cancel()
   256  	mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
   257  	if err != nil {
   258  		return ErrorWithContext(ctx, err)
   259  	}
   260  	userName, err := utils.DecodeBase64(params.Name)
   261  	if err != nil {
   262  		return ErrorWithContext(ctx, err)
   263  	}
   264  	if session.AccountAccessKey == userName {
   265  		return ErrorWithContext(ctx, ErrAvoidSelfAccountDelete)
   266  	}
   267  	// create a minioClient interface implementation
   268  	// defining the client to be used
   269  	adminClient := AdminClient{Client: mAdmin}
   270  	if err := removeUser(ctx, adminClient, userName); err != nil {
   271  		return ErrorWithContext(ctx, err)
   272  	}
   273  	return nil
   274  }
   275  
   276  // getUserInfo calls MinIO server get the User Information
   277  func getUserInfo(ctx context.Context, client MinioAdmin, accessKey string) (*madmin.UserInfo, error) {
   278  	userInfo, err := client.getUserInfo(ctx, accessKey)
   279  	if err != nil {
   280  		return nil, err
   281  	}
   282  	return &userInfo, nil
   283  }
   284  
   285  func getUserInfoResponse(session *models.Principal, params userApi.GetUserInfoParams) (*models.User, *CodedAPIError) {
   286  	ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
   287  	defer cancel()
   288  
   289  	mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
   290  	if err != nil {
   291  		return nil, ErrorWithContext(ctx, err)
   292  	}
   293  
   294  	// create a minioClient interface implementation
   295  	// defining the client to be used
   296  	adminClient := AdminClient{Client: mAdmin}
   297  
   298  	userName, err := utils.DecodeBase64(params.Name)
   299  	if err != nil {
   300  		return nil, ErrorWithContext(ctx, err)
   301  	}
   302  
   303  	user, err := getUserInfo(ctx, adminClient, userName)
   304  	if err != nil {
   305  		// User doesn't exist, return 404
   306  		if madmin.ToErrorResponse(err).Code == "XMinioAdminNoSuchUser" {
   307  			errorCode := 404
   308  			errorMessage := "User doesn't exist"
   309  			return nil, &CodedAPIError{Code: errorCode, APIError: &models.APIError{Message: errorMessage, DetailedMessage: err.Error()}}
   310  		}
   311  		return nil, ErrorWithContext(ctx, err)
   312  	}
   313  
   314  	var policies []string
   315  	if user.PolicyName == "" {
   316  		policies = []string{}
   317  	} else {
   318  		policies = strings.Split(user.PolicyName, ",")
   319  	}
   320  
   321  	hasPolicy := true
   322  
   323  	if len(policies) == 0 {
   324  		hasPolicy = false
   325  		for i := 0; i < len(user.MemberOf); i++ {
   326  			group, err := adminClient.getGroupDescription(ctx, user.MemberOf[i])
   327  			if err != nil {
   328  				continue
   329  			}
   330  			if group.Policy != "" {
   331  				hasPolicy = true
   332  				break
   333  			}
   334  		}
   335  	}
   336  
   337  	userInformation := &models.User{
   338  		AccessKey: userName,
   339  		MemberOf:  user.MemberOf,
   340  		Policy:    policies,
   341  		Status:    string(user.Status),
   342  		HasPolicy: hasPolicy,
   343  	}
   344  
   345  	return userInformation, nil
   346  }
   347  
   348  // updateUserGroups invokes getUserInfo() to get the old groups from the user,
   349  // 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.
   350  // 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
   351  func updateUserGroups(ctx context.Context, client MinioAdmin, user string, groupsToAssign []string) (*models.User, error) {
   352  	parallelUserUpdate := func(groupName string, originGroups []string) chan error {
   353  		chProcess := make(chan error)
   354  
   355  		go func() error {
   356  			defer close(chProcess)
   357  
   358  			// Compare if groupName is in the arrays
   359  			isGroupPersistent := IsElementInArray(groupsToAssign, groupName)
   360  			isInOriginGroups := IsElementInArray(originGroups, groupName)
   361  
   362  			if isGroupPersistent && isInOriginGroups { // Group is already assigned and doesn't need to be updated
   363  				chProcess <- nil
   364  
   365  				return nil
   366  			}
   367  
   368  			isRemove := false // User is added by default
   369  
   370  			// User is deleted from the group
   371  			if !isGroupPersistent {
   372  				isRemove = true
   373  			}
   374  
   375  			userToAddRemove := []string{user}
   376  
   377  			updateReturn := updateGroupMembers(ctx, client, groupName, userToAddRemove, isRemove)
   378  
   379  			chProcess <- updateReturn
   380  
   381  			return updateReturn
   382  		}()
   383  
   384  		return chProcess
   385  	}
   386  
   387  	userInfoOr, err := getUserInfo(ctx, client, user)
   388  	if err != nil {
   389  		return nil, err
   390  	}
   391  
   392  	memberOf := userInfoOr.MemberOf
   393  	mergedGroupArray := UniqueKeys(append(memberOf, groupsToAssign...))
   394  
   395  	var listOfUpdates []chan error
   396  
   397  	// Each group must be updated individually because there is no way to update all the groups at once for a user,
   398  	// we are using the same logic as 'mc admin group add' command
   399  	for _, groupN := range mergedGroupArray {
   400  		proc := parallelUserUpdate(groupN, memberOf)
   401  		listOfUpdates = append(listOfUpdates, proc)
   402  	}
   403  
   404  	channelHasError := false
   405  
   406  	for _, chanRet := range listOfUpdates {
   407  		locError := <-chanRet
   408  
   409  		if locError != nil {
   410  			channelHasError = true
   411  		}
   412  	}
   413  
   414  	if channelHasError {
   415  		errRt := errors.New(500, "there was an error updating the groups")
   416  		return nil, errRt
   417  	}
   418  
   419  	userInfo, err := getUserInfo(ctx, client, user)
   420  	if err != nil {
   421  		return nil, err
   422  	}
   423  
   424  	policies := strings.Split(userInfo.PolicyName, ",")
   425  
   426  	userReturn := &models.User{
   427  		AccessKey: user,
   428  		MemberOf:  userInfo.MemberOf,
   429  		Policy:    policies,
   430  		Status:    string(userInfo.Status),
   431  	}
   432  
   433  	return userReturn, nil
   434  }
   435  
   436  func getUpdateUserGroupsResponse(session *models.Principal, params userApi.UpdateUserGroupsParams) (*models.User, *CodedAPIError) {
   437  	ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
   438  	defer cancel()
   439  
   440  	mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
   441  	if err != nil {
   442  		return nil, ErrorWithContext(ctx, err)
   443  	}
   444  
   445  	// create a minioClient interface implementation
   446  	// defining the client to be used
   447  	adminClient := AdminClient{Client: mAdmin}
   448  
   449  	userName, err := utils.DecodeBase64(params.Name)
   450  	if err != nil {
   451  		return nil, ErrorWithContext(ctx, err)
   452  	}
   453  
   454  	user, err := updateUserGroups(ctx, adminClient, userName, params.Body.Groups)
   455  	if err != nil {
   456  		return nil, ErrorWithContext(ctx, err)
   457  	}
   458  
   459  	return user, nil
   460  }
   461  
   462  // setUserStatus invokes setUserStatus from madmin to update user status
   463  func setUserStatus(ctx context.Context, client MinioAdmin, user string, status string) error {
   464  	var setStatus madmin.AccountStatus
   465  	switch status {
   466  	case "enabled":
   467  		setStatus = madmin.AccountEnabled
   468  	case "disabled":
   469  		setStatus = madmin.AccountDisabled
   470  	default:
   471  		return errors.New(500, "status not valid")
   472  	}
   473  
   474  	return client.setUserStatus(ctx, user, setStatus)
   475  }
   476  
   477  func getUpdateUserResponse(session *models.Principal, params userApi.UpdateUserInfoParams) (*models.User, *CodedAPIError) {
   478  	ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
   479  	defer cancel()
   480  
   481  	mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
   482  	if err != nil {
   483  		return nil, ErrorWithContext(ctx, err)
   484  	}
   485  
   486  	// create a minioClient interface implementation
   487  	// defining the client to be used
   488  	adminClient := AdminClient{Client: mAdmin}
   489  	userName, err := utils.DecodeBase64(params.Name)
   490  	if err != nil {
   491  		return nil, ErrorWithContext(ctx, err)
   492  	}
   493  	status := *params.Body.Status
   494  	groups := params.Body.Groups
   495  
   496  	if err := setUserStatus(ctx, adminClient, userName, status); err != nil {
   497  		return nil, ErrorWithContext(ctx, err)
   498  	}
   499  
   500  	userElem, errUG := updateUserGroups(ctx, adminClient, userName, groups)
   501  
   502  	if errUG != nil {
   503  		return nil, ErrorWithContext(ctx, errUG)
   504  	}
   505  	return userElem, nil
   506  }
   507  
   508  // addUsersListToGroups iterates over the user list & assigns the requested groups to each user.
   509  func addUsersListToGroups(ctx context.Context, client MinioAdmin, usersToUpdate []string, groupsToAssign []string) error {
   510  	// We update each group with the complete usersList
   511  	parallelGroupsUpdate := func(groupToAssign string) chan error {
   512  		groupProcess := make(chan error)
   513  
   514  		go func() {
   515  			defer close(groupProcess)
   516  			// We add the users array to the group.
   517  			err := updateGroupMembers(ctx, client, groupToAssign, usersToUpdate, false)
   518  
   519  			groupProcess <- err
   520  		}()
   521  		return groupProcess
   522  	}
   523  
   524  	var groupsUpdateList []chan error
   525  
   526  	// We get each group name & add users accordingly
   527  	for _, groupName := range groupsToAssign {
   528  		// We update the group
   529  		proc := parallelGroupsUpdate(groupName)
   530  		groupsUpdateList = append(groupsUpdateList, proc)
   531  	}
   532  
   533  	errorsList := []string{} // We get the errors list because we want to have all errors at once.
   534  	for _, err := range groupsUpdateList {
   535  		errorFromUpdate := <-err // We store the errors to avoid Data Race
   536  		if errorFromUpdate != nil {
   537  			// If there is an errors, we store the errors strings so we can join them after we receive all errors
   538  			errorsList = append(errorsList, errorFromUpdate.Error()) // We wait until all the channels have been closed.
   539  		}
   540  	}
   541  
   542  	// If there are errors, we throw the final errors with the errors inside
   543  	if len(errorsList) > 0 {
   544  		errGen := fmt.Errorf("error in users-groups assignation: %q", strings.Join(errorsList, ","))
   545  		return errGen
   546  	}
   547  
   548  	return nil
   549  }
   550  
   551  func getAddUsersListToGroupsResponse(session *models.Principal, params userApi.BulkUpdateUsersGroupsParams) *CodedAPIError {
   552  	ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
   553  	defer cancel()
   554  
   555  	mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
   556  	if err != nil {
   557  		return ErrorWithContext(ctx, err)
   558  	}
   559  
   560  	// create a minioClient interface implementation
   561  	// defining the client to be used
   562  	adminClient := AdminClient{Client: mAdmin}
   563  
   564  	usersList := params.Body.Users
   565  	groupsList := params.Body.Groups
   566  
   567  	if err := addUsersListToGroups(ctx, adminClient, usersList, groupsList); err != nil {
   568  		return ErrorWithContext(ctx, err)
   569  	}
   570  
   571  	return nil
   572  }
   573  
   574  func getListUsersWithAccessToBucketResponse(session *models.Principal, params bucketApi.ListUsersWithAccessToBucketParams) ([]string, *CodedAPIError) {
   575  	ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
   576  	defer cancel()
   577  	mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
   578  	if err != nil {
   579  		return nil, ErrorWithContext(ctx, err)
   580  	}
   581  	// create a minioClient interface implementation
   582  	// defining the client to be used
   583  	adminClient := AdminClient{Client: mAdmin}
   584  	list, err := listUsersWithAccessToBucket(ctx, adminClient, params.Bucket)
   585  	if err != nil {
   586  		return nil, ErrorWithContext(ctx, err)
   587  	}
   588  	return list, nil
   589  }
   590  
   591  func policyAllowsAndMatchesBucket(policy *iampolicy.Policy, bucket string) int {
   592  	policyStatements := policy.Statements
   593  	for i := 0; i < len(policyStatements); i++ {
   594  		resources := policyStatements[i].Resources
   595  		effect := policyStatements[i].Effect
   596  		if resources.Match(bucket, map[string][]string{}) {
   597  			if effect.IsValid() {
   598  				if effect.IsAllowed(true) {
   599  					return Allow
   600  				}
   601  				return Deny
   602  			}
   603  		}
   604  	}
   605  	return Unknown
   606  }
   607  
   608  func listUsersWithAccessToBucket(ctx context.Context, adminClient MinioAdmin, bucket string) ([]string, error) {
   609  	users, err := adminClient.listUsers(ctx)
   610  	if err != nil {
   611  		return nil, err
   612  	}
   613  	var retval []string
   614  	akHasAccess := make(map[string]struct{})
   615  	akIsDenied := make(map[string]struct{})
   616  	for k, v := range users {
   617  		for _, policyName := range strings.Split(v.PolicyName, ",") {
   618  			policyName = strings.TrimSpace(policyName)
   619  			if policyName == "" {
   620  				continue
   621  			}
   622  			policy, err := adminClient.getPolicy(ctx, policyName)
   623  			if err != nil {
   624  				ErrorWithContext(ctx, fmt.Errorf("unable to fetch policy %s: %v", policyName, err))
   625  				continue
   626  			}
   627  			if _, ok := akIsDenied[k]; !ok {
   628  				switch policyAllowsAndMatchesBucket(policy, bucket) {
   629  				case Allow:
   630  					if _, ok := akHasAccess[k]; !ok {
   631  						akHasAccess[k] = struct{}{}
   632  					}
   633  				case Deny:
   634  					akIsDenied[k] = struct{}{}
   635  					delete(akHasAccess, k)
   636  				}
   637  			}
   638  		}
   639  	}
   640  
   641  	groups, err := adminClient.listGroups(ctx)
   642  	if err != nil {
   643  		ErrorWithContext(ctx, fmt.Errorf("unable to list groups: %v", err))
   644  		return retval, nil
   645  	}
   646  
   647  	for _, groupName := range groups {
   648  		info, err := groupInfo(ctx, adminClient, groupName)
   649  		if err != nil {
   650  			ErrorWithContext(ctx, fmt.Errorf("unable to fetch group info %s: %v", groupName, err))
   651  			continue
   652  		}
   653  		policy, err := adminClient.getPolicy(ctx, info.Policy)
   654  		if err != nil {
   655  			ErrorWithContext(ctx, fmt.Errorf("unable to fetch group policy %s: %v", info.Policy, err))
   656  			continue
   657  		}
   658  		for _, member := range info.Members {
   659  			if _, ok := akIsDenied[member]; !ok {
   660  				switch policyAllowsAndMatchesBucket(policy, bucket) {
   661  				case Allow:
   662  					if _, ok := akHasAccess[member]; !ok {
   663  						akHasAccess[member] = struct{}{}
   664  					}
   665  				case Deny:
   666  					akIsDenied[member] = struct{}{}
   667  					delete(akHasAccess, member)
   668  				}
   669  			}
   670  		}
   671  	}
   672  	for k := range akHasAccess {
   673  		retval = append(retval, k)
   674  	}
   675  	sort.Strings(retval)
   676  	return retval, nil
   677  }
   678  
   679  // changeUserPassword changes password of selectedUser to newSecretKey
   680  func changeUserPassword(ctx context.Context, client MinioAdmin, selectedUser string, newSecretKey string) error {
   681  	return client.changePassword(ctx, selectedUser, newSecretKey)
   682  }
   683  
   684  // getChangeUserPasswordResponse will change the password of selctedUser to newSecretKey
   685  func getChangeUserPasswordResponse(session *models.Principal, params accountApi.ChangeUserPasswordParams) *CodedAPIError {
   686  	ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
   687  	defer cancel()
   688  	mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
   689  	if err != nil {
   690  		return ErrorWithContext(ctx, err)
   691  	}
   692  	// create a minioClient interface implementation
   693  	// defining the client to be used
   694  	adminClient := AdminClient{Client: mAdmin}
   695  
   696  	// params will contain selectedUser and newSecretKey credentials for the user
   697  	user := *params.Body.SelectedUser
   698  	newSecretKey := *params.Body.NewSecretKey
   699  
   700  	// changes password of user to newSecretKey
   701  	if err := changeUserPassword(ctx, adminClient, user, newSecretKey); err != nil {
   702  		return ErrorWithContext(ctx, err)
   703  	}
   704  	return nil
   705  }
   706  
   707  func getCheckUserSAResponse(session *models.Principal, params userApi.CheckUserServiceAccountsParams) (*models.UserServiceAccountSummary, *CodedAPIError) {
   708  	ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
   709  	defer cancel()
   710  	mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session)
   711  	if err != nil {
   712  		return nil, ErrorWithContext(ctx, err)
   713  	}
   714  	// create a minioClient interface implementation
   715  	// defining the client to be used
   716  	adminClient := AdminClient{Client: mAdmin}
   717  
   718  	var userServiceAccountList []*models.UserServiceAccountItem
   719  	hasSA := false
   720  	for _, user := range params.SelectedUsers {
   721  		listServAccs, err := adminClient.listServiceAccounts(ctx, user)
   722  		if err != nil {
   723  			return nil, ErrorWithContext(ctx, err)
   724  		}
   725  		numSAs := int64(len(listServAccs.Accounts))
   726  		if numSAs > 0 {
   727  			hasSA = true
   728  		}
   729  		userAccountItem := &models.UserServiceAccountItem{
   730  			UserName: user,
   731  			NumSAs:   numSAs,
   732  		}
   733  		userServiceAccountList = append(userServiceAccountList, userAccountItem)
   734  	}
   735  
   736  	userAccountList := &models.UserServiceAccountSummary{
   737  		UserServiceAccountList: userServiceAccountList,
   738  		HasSA:                  hasSA,
   739  	}
   740  
   741  	return userAccountList, nil
   742  }