2020-04-02 12:51:51 -07:00
// This file is part of MinIO Console Server
2021-01-19 17:04:13 -06:00
// Copyright (c) 2021 MinIO, Inc.
2020-04-01 18:18:57 -07:00
//
// 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 (
2021-06-01 11:34:55 -07:00
"context"
"fmt"
2021-05-28 18:28:39 -07:00
"sort"
2021-06-01 11:34:55 -07:00
"strings"
2021-05-28 18:28:39 -07:00
2022-05-13 13:13:56 -07:00
"github.com/minio/console/pkg/utils"
2021-09-14 15:23:38 -07:00
"github.com/go-openapi/swag"
2020-04-11 00:13:31 -05:00
"github.com/go-openapi/errors"
2020-04-01 18:18:57 -07:00
"github.com/go-openapi/runtime/middleware"
2020-07-26 00:34:17 -07:00
"github.com/minio/console/models"
"github.com/minio/console/restapi/operations"
2022-04-27 11:45:04 -07:00
accountApi "github.com/minio/console/restapi/operations/account"
bucketApi "github.com/minio/console/restapi/operations/bucket"
userApi "github.com/minio/console/restapi/operations/user"
2021-06-01 11:34:55 -07:00
"github.com/minio/madmin-go"
iampolicy "github.com/minio/pkg/iam/policy"
2020-04-01 18:18:57 -07:00
)
2021-06-01 11:34:55 -07:00
// Policy evaluated constants
const (
Unknown = 0
Allow = 1
Deny = - 1
)
2021-05-28 18:28:39 -07:00
2020-07-26 00:34:17 -07:00
func registerUsersHandlers ( api * operations . ConsoleAPI ) {
2020-04-01 18:18:57 -07:00
// List Users
2022-04-27 11:45:04 -07:00
api . UserListUsersHandler = userApi . ListUsersHandlerFunc ( func ( params userApi . ListUsersParams , session * models . Principal ) middleware . Responder {
2022-04-28 12:55:06 -07:00
listUsersResponse , err := getListUsersResponse ( session , params )
2020-04-01 18:18:57 -07:00
if err != nil {
2022-04-27 11:45:04 -07:00
return userApi . NewListUsersDefault ( int ( err . Code ) ) . WithPayload ( err )
2020-04-01 18:18:57 -07:00
}
2022-04-27 11:45:04 -07:00
return userApi . NewListUsersOK ( ) . WithPayload ( listUsersResponse )
2020-04-01 18:18:57 -07:00
} )
// Add User
2022-04-27 11:45:04 -07:00
api . UserAddUserHandler = userApi . AddUserHandlerFunc ( func ( params userApi . AddUserParams , session * models . Principal ) middleware . Responder {
2020-07-10 19:14:28 -07:00
userResponse , err := getUserAddResponse ( session , params )
2020-04-01 18:18:57 -07:00
if err != nil {
2022-04-27 11:45:04 -07:00
return userApi . NewAddUserDefault ( int ( err . Code ) ) . WithPayload ( err )
2020-04-01 18:18:57 -07:00
}
2022-04-27 11:45:04 -07:00
return userApi . NewAddUserCreated ( ) . WithPayload ( userResponse )
2020-04-01 18:18:57 -07:00
} )
2020-04-06 20:07:32 -05:00
// Remove User
2022-04-27 11:45:04 -07:00
api . UserRemoveUserHandler = userApi . RemoveUserHandlerFunc ( func ( params userApi . RemoveUserParams , session * models . Principal ) middleware . Responder {
2020-07-10 19:14:28 -07:00
err := getRemoveUserResponse ( session , params )
2020-04-06 20:07:32 -05:00
if err != nil {
2022-04-27 11:45:04 -07:00
return userApi . NewRemoveUserDefault ( int ( err . Code ) ) . WithPayload ( err )
2020-04-06 20:07:32 -05:00
}
2022-04-27 11:45:04 -07:00
return userApi . NewRemoveUserNoContent ( )
2020-04-06 20:07:32 -05:00
} )
2020-04-08 19:38:18 -05:00
// Update User-Groups
2022-04-27 11:45:04 -07:00
api . UserUpdateUserGroupsHandler = userApi . UpdateUserGroupsHandlerFunc ( func ( params userApi . UpdateUserGroupsParams , session * models . Principal ) middleware . Responder {
2020-07-10 19:14:28 -07:00
userUpdateResponse , err := getUpdateUserGroupsResponse ( session , params )
2020-04-08 19:38:18 -05:00
if err != nil {
2022-04-27 11:45:04 -07:00
return userApi . NewUpdateUserGroupsDefault ( int ( err . Code ) ) . WithPayload ( err )
2020-04-08 19:38:18 -05:00
}
2022-04-27 11:45:04 -07:00
return userApi . NewUpdateUserGroupsOK ( ) . WithPayload ( userUpdateResponse )
2020-04-08 19:38:18 -05:00
} )
2020-04-09 18:39:49 -05:00
// Get User
2022-04-27 11:45:04 -07:00
api . UserGetUserInfoHandler = userApi . GetUserInfoHandlerFunc ( func ( params userApi . GetUserInfoParams , session * models . Principal ) middleware . Responder {
2020-07-10 19:14:28 -07:00
userInfoResponse , err := getUserInfoResponse ( session , params )
2020-04-09 18:39:49 -05:00
if err != nil {
2022-04-27 11:45:04 -07:00
return userApi . NewGetUserInfoDefault ( int ( err . Code ) ) . WithPayload ( err )
2020-04-09 18:39:49 -05:00
}
2022-04-27 11:45:04 -07:00
return userApi . NewGetUserInfoOK ( ) . WithPayload ( userInfoResponse )
2020-04-09 18:39:49 -05:00
} )
2020-04-11 00:13:31 -05:00
// Update User
2022-04-27 11:45:04 -07:00
api . UserUpdateUserInfoHandler = userApi . UpdateUserInfoHandlerFunc ( func ( params userApi . UpdateUserInfoParams , session * models . Principal ) middleware . Responder {
2020-07-10 19:14:28 -07:00
userUpdateResponse , err := getUpdateUserResponse ( session , params )
2020-04-11 00:13:31 -05:00
if err != nil {
2022-04-27 11:45:04 -07:00
return userApi . NewUpdateUserInfoDefault ( int ( err . Code ) ) . WithPayload ( err )
2020-04-11 00:13:31 -05:00
}
2022-04-27 11:45:04 -07:00
return userApi . NewUpdateUserInfoOK ( ) . WithPayload ( userUpdateResponse )
2020-04-11 00:13:31 -05:00
} )
2020-04-15 20:08:35 -05:00
// Update User-Groups Bulk
2022-04-27 11:45:04 -07:00
api . UserBulkUpdateUsersGroupsHandler = userApi . BulkUpdateUsersGroupsHandlerFunc ( func ( params userApi . BulkUpdateUsersGroupsParams , session * models . Principal ) middleware . Responder {
2020-07-10 19:14:28 -07:00
err := getAddUsersListToGroupsResponse ( session , params )
2020-04-22 23:43:17 -07:00
if err != nil {
2022-04-27 11:45:04 -07:00
return userApi . NewBulkUpdateUsersGroupsDefault ( int ( err . Code ) ) . WithPayload ( err )
2020-04-15 20:08:35 -05:00
}
2022-04-27 11:45:04 -07:00
return userApi . NewBulkUpdateUsersGroupsOK ( )
2020-04-15 20:08:35 -05:00
} )
2022-04-27 11:45:04 -07:00
api . BucketListUsersWithAccessToBucketHandler = bucketApi . ListUsersWithAccessToBucketHandlerFunc ( func ( params bucketApi . ListUsersWithAccessToBucketParams , session * models . Principal ) middleware . Responder {
2022-04-28 12:55:06 -07:00
response , err := getListUsersWithAccessToBucketResponse ( session , params )
2021-04-02 16:13:29 -07:00
if err != nil {
2022-04-27 11:45:04 -07:00
return bucketApi . NewListUsersWithAccessToBucketDefault ( int ( err . Code ) ) . WithPayload ( err )
2021-04-02 16:13:29 -07:00
}
2022-04-27 11:45:04 -07:00
return bucketApi . NewListUsersWithAccessToBucketOK ( ) . WithPayload ( response )
2021-04-02 16:13:29 -07:00
} )
2021-06-03 15:32:22 -07:00
// Change User Password
2022-04-27 11:45:04 -07:00
api . AccountChangeUserPasswordHandler = accountApi . ChangeUserPasswordHandlerFunc ( func ( params accountApi . ChangeUserPasswordParams , session * models . Principal ) middleware . Responder {
2021-06-03 15:32:22 -07:00
err := getChangeUserPasswordResponse ( session , params )
if err != nil {
2022-04-27 11:45:04 -07:00
return accountApi . NewChangeUserPasswordDefault ( int ( err . Code ) ) . WithPayload ( err )
2021-06-03 15:32:22 -07:00
}
2022-04-27 11:45:04 -07:00
return accountApi . NewChangeUserPasswordCreated ( )
2021-06-03 15:32:22 -07:00
} )
2022-05-22 22:14:24 -07:00
// Check number of Service Accounts for listed users
api . UserCheckUserServiceAccountsHandler = userApi . CheckUserServiceAccountsHandlerFunc ( func ( params userApi . CheckUserServiceAccountsParams , session * models . Principal ) middleware . Responder {
userSAList , err := getCheckUserSAResponse ( session , params )
if err != nil {
return userApi . NewCheckUserServiceAccountsDefault ( int ( err . Code ) ) . WithPayload ( err )
}
return userApi . NewCheckUserServiceAccountsOK ( ) . WithPayload ( userSAList )
} )
2020-04-01 18:18:57 -07:00
}
2020-04-06 20:07:32 -05:00
func listUsers ( ctx context . Context , client MinioAdmin ) ( [ ] * models . User , error ) {
2020-04-01 18:18:57 -07:00
// Get list of all users in the MinIO
// This call requires explicit authentication, no anonymous requests are
// allowed for listing users.
userMap , err := client . listUsers ( ctx )
if err != nil {
return [ ] * models . User { } , err
}
var users [ ] * models . User
for accessKey , user := range userMap {
userElem := & models . User {
AccessKey : accessKey ,
Status : string ( user . Status ) ,
2021-05-08 00:00:29 -07:00
Policy : strings . Split ( user . PolicyName , "," ) ,
2020-04-01 18:18:57 -07:00
MemberOf : user . MemberOf ,
}
users = append ( users , userElem )
}
return users , nil
}
// getListUsersResponse performs listUsers() and serializes it to the handler's output
2022-04-28 12:55:06 -07:00
func getListUsersResponse ( session * models . Principal , params userApi . ListUsersParams ) ( * models . ListUsersResponse , * models . Error ) {
ctx , cancel := context . WithCancel ( params . HTTPRequest . Context ( ) )
2022-04-11 16:20:30 -07:00
defer cancel ( )
2021-07-19 11:48:50 -07:00
mAdmin , err := NewMinioAdminClient ( session )
2020-04-01 18:18:57 -07:00
if err != nil {
2022-04-28 12:55:06 -07:00
return nil , ErrorWithContext ( ctx , err )
2020-04-01 18:18:57 -07:00
}
// create a minioClient interface implementation
// defining the client to be used
2021-07-19 11:48:50 -07:00
adminClient := AdminClient { Client : mAdmin }
2020-04-06 20:07:32 -05:00
users , err := listUsers ( ctx , adminClient )
2020-04-01 18:18:57 -07:00
if err != nil {
2022-04-28 12:55:06 -07:00
return nil , ErrorWithContext ( ctx , err )
2020-04-01 18:18:57 -07:00
}
// serialize output
listUsersResponse := & models . ListUsersResponse {
Users : users ,
}
return listUsersResponse , nil
}
// addUser invokes adding a users on `MinioAdmin` and builds the response `models.User`
2021-08-06 16:41:38 -07:00
func addUser ( ctx context . Context , client MinioAdmin , accessKey , secretKey * string , groups [ ] string , policies [ ] string ) ( * models . User , error ) {
2022-04-28 12:55:06 -07:00
// Calls into MinIO to add a new user if there's an errors return it
2020-04-09 18:39:49 -05:00
if err := client . addUser ( ctx , * accessKey , * secretKey ) ; err != nil {
2020-04-01 18:18:57 -07:00
return nil , err
}
2021-08-06 16:41:38 -07:00
// set groups for the newly created user
var userWithGroups * models . User
2020-04-09 18:39:49 -05:00
if len ( groups ) > 0 {
2021-08-06 16:41:38 -07:00
var errUG error
userWithGroups , errUG = updateUserGroups ( ctx , client , * accessKey , groups )
2020-04-09 18:39:49 -05:00
if errUG != nil {
return nil , errUG
}
2021-08-06 16:41:38 -07:00
}
// set policies for the newly created user
if len ( policies ) > 0 {
policyString := strings . Join ( policies , "," )
2022-07-20 18:27:11 -07:00
if err := SetPolicy ( ctx , client , policyString , * accessKey , "user" ) ; err != nil {
2021-08-06 16:41:38 -07:00
return nil , err
}
}
memberOf := [ ] string { }
status := "enabled"
if userWithGroups != nil {
memberOf = userWithGroups . MemberOf
status = userWithGroups . Status
2020-04-01 18:18:57 -07:00
}
2020-04-09 18:39:49 -05:00
userRet := & models . User {
AccessKey : * accessKey ,
2021-08-06 16:41:38 -07:00
MemberOf : memberOf ,
Policy : policies ,
Status : status ,
2020-04-09 18:39:49 -05:00
}
return userRet , nil
2020-04-01 18:18:57 -07:00
}
2022-04-27 11:45:04 -07:00
func getUserAddResponse ( session * models . Principal , params userApi . AddUserParams ) ( * models . User , * models . Error ) {
2022-04-28 12:55:06 -07:00
ctx , cancel := context . WithCancel ( params . HTTPRequest . Context ( ) )
2022-04-11 16:20:30 -07:00
defer cancel ( )
2021-07-19 11:48:50 -07:00
mAdmin , err := NewMinioAdminClient ( session )
2020-04-01 18:18:57 -07:00
if err != nil {
2022-04-28 12:55:06 -07:00
return nil , ErrorWithContext ( ctx , err )
2020-04-01 18:18:57 -07:00
}
// create a minioClient interface implementation
// defining the client to be used
2021-07-19 11:48:50 -07:00
adminClient := AdminClient { Client : mAdmin }
2021-10-12 11:18:56 -07:00
var userExists bool
2020-04-01 18:18:57 -07:00
2021-10-12 11:18:56 -07:00
_ , err = adminClient . getUserInfo ( ctx , * params . Body . AccessKey )
userExists = err == nil
if userExists {
2022-04-28 12:55:06 -07:00
return nil , ErrorWithContext ( ctx , ErrNonUniqueAccessKey )
2021-10-12 11:18:56 -07:00
}
2021-08-06 16:41:38 -07:00
user , err := addUser (
ctx ,
adminClient ,
params . Body . AccessKey ,
params . Body . SecretKey ,
params . Body . Groups ,
params . Body . Policies ,
)
2020-04-01 18:18:57 -07:00
if err != nil {
2022-04-28 12:55:06 -07:00
return nil , ErrorWithContext ( ctx , err )
2020-04-01 18:18:57 -07:00
}
return user , nil
}
2020-04-06 20:07:32 -05:00
2022-05-05 13:44:10 -07:00
// removeUser invokes removing an user on `MinioAdmin`, then we return the response from API
2020-04-06 20:07:32 -05:00
func removeUser ( ctx context . Context , client MinioAdmin , accessKey string ) error {
2021-06-07 20:53:03 -07:00
return client . removeUser ( ctx , accessKey )
2020-04-06 20:07:32 -05:00
}
2022-04-27 11:45:04 -07:00
func getRemoveUserResponse ( session * models . Principal , params userApi . RemoveUserParams ) * models . Error {
2022-04-28 12:55:06 -07:00
ctx , cancel := context . WithCancel ( params . HTTPRequest . Context ( ) )
2022-04-11 16:20:30 -07:00
defer cancel ( )
2021-07-19 11:48:50 -07:00
mAdmin , err := NewMinioAdminClient ( session )
2020-04-06 20:07:32 -05:00
if err != nil {
2022-04-28 12:55:06 -07:00
return ErrorWithContext ( ctx , err )
2020-04-06 20:07:32 -05:00
}
2022-05-13 13:13:56 -07:00
userName , err := utils . DecodeBase64 ( params . Name )
if err != nil {
return ErrorWithContext ( ctx , err )
}
if session . AccountAccessKey == userName {
2022-04-28 12:55:06 -07:00
return ErrorWithContext ( ctx , ErrAvoidSelfAccountDelete )
2021-03-08 23:38:19 -06:00
}
2020-04-06 20:07:32 -05:00
// create a minioClient interface implementation
// defining the client to be used
2021-07-19 11:48:50 -07:00
adminClient := AdminClient { Client : mAdmin }
2022-05-13 13:13:56 -07:00
if err := removeUser ( ctx , adminClient , userName ) ; err != nil {
2022-04-28 12:55:06 -07:00
return ErrorWithContext ( ctx , err )
2020-04-06 20:07:32 -05:00
}
return nil
}
2020-04-08 19:38:18 -05:00
// getUserInfo calls MinIO server get the User Information
func getUserInfo ( ctx context . Context , client MinioAdmin , accessKey string ) ( * madmin . UserInfo , error ) {
userInfo , err := client . getUserInfo ( ctx , accessKey )
if err != nil {
return nil , err
}
return & userInfo , nil
}
2022-04-27 11:45:04 -07:00
func getUserInfoResponse ( session * models . Principal , params userApi . GetUserInfoParams ) ( * models . User , * models . Error ) {
2022-04-28 12:55:06 -07:00
ctx , cancel := context . WithCancel ( params . HTTPRequest . Context ( ) )
2022-04-11 16:20:30 -07:00
defer cancel ( )
2020-04-09 18:39:49 -05:00
2021-07-19 11:48:50 -07:00
mAdmin , err := NewMinioAdminClient ( session )
2020-04-09 18:39:49 -05:00
if err != nil {
2022-04-28 12:55:06 -07:00
return nil , ErrorWithContext ( ctx , err )
2020-04-09 18:39:49 -05:00
}
// create a minioClient interface implementation
// defining the client to be used
2021-07-19 11:48:50 -07:00
adminClient := AdminClient { Client : mAdmin }
2020-04-09 18:39:49 -05:00
2022-05-13 13:13:56 -07:00
userName , err := utils . DecodeBase64 ( params . Name )
if err != nil {
return nil , ErrorWithContext ( ctx , err )
}
user , err := getUserInfo ( ctx , adminClient , userName )
2020-04-09 18:39:49 -05:00
if err != nil {
2021-09-14 15:23:38 -07:00
// User doesn't exist, return 404
if madmin . ToErrorResponse ( err ) . Code == "XMinioAdminNoSuchUser" {
var errorCode int32 = 404
errorMessage := "User doesn't exist"
return nil , & models . Error { Code : errorCode , Message : swag . String ( errorMessage ) , DetailedMessage : swag . String ( err . Error ( ) ) }
}
2022-04-28 12:55:06 -07:00
return nil , ErrorWithContext ( ctx , err )
2020-04-09 18:39:49 -05:00
}
2021-09-07 15:02:04 -07:00
var policies [ ] string
if user . PolicyName == "" {
policies = [ ] string { }
} else {
policies = strings . Split ( user . PolicyName , "," )
}
hasPolicy := true
if len ( policies ) == 0 {
hasPolicy = false
for i := 0 ; i < len ( user . MemberOf ) ; i ++ {
group , err := adminClient . getGroupDescription ( ctx , user . MemberOf [ i ] )
if err != nil {
continue
}
if group . Policy != "" {
hasPolicy = true
break
}
}
}
2020-04-09 18:39:49 -05:00
userInformation := & models . User {
2022-05-13 13:13:56 -07:00
AccessKey : userName ,
2020-04-09 18:39:49 -05:00
MemberOf : user . MemberOf ,
2021-09-07 15:02:04 -07:00
Policy : policies ,
2020-04-09 18:39:49 -05:00
Status : string ( user . Status ) ,
2021-09-07 15:02:04 -07:00
HasPolicy : hasPolicy ,
2020-04-09 18:39:49 -05:00
}
return userInformation , nil
}
2020-04-08 19:38:18 -05:00
// 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
func updateUserGroups ( ctx context . Context , client MinioAdmin , user string , groupsToAssign [ ] string ) ( * models . User , error ) {
parallelUserUpdate := func ( groupName string , originGroups [ ] string ) chan error {
chProcess := make ( chan error )
go func ( ) error {
defer close ( chProcess )
2022-05-05 13:44:10 -07:00
// Compare if groupName is in the arrays
2020-04-08 19:38:18 -05:00
isGroupPersistent := IsElementInArray ( groupsToAssign , groupName )
isInOriginGroups := IsElementInArray ( originGroups , groupName )
if isGroupPersistent && isInOriginGroups { // Group is already assigned and doesn't need to be updated
chProcess <- nil
return nil
}
isRemove := false // User is added by default
// User is deleted from the group
if ! isGroupPersistent {
isRemove = true
}
userToAddRemove := [ ] string { user }
updateReturn := updateGroupMembers ( ctx , client , groupName , userToAddRemove , isRemove )
chProcess <- updateReturn
return updateReturn
} ( )
return chProcess
}
userInfoOr , err := getUserInfo ( ctx , client , user )
if err != nil {
return nil , err
}
memberOf := userInfoOr . MemberOf
mergedGroupArray := UniqueKeys ( append ( memberOf , groupsToAssign ... ) )
var listOfUpdates [ ] chan error
// Each group must be updated individually because there is no way to update all the groups at once for a user,
// we are using the same logic as 'mc admin group add' command
for _ , groupN := range mergedGroupArray {
proc := parallelUserUpdate ( groupN , memberOf )
listOfUpdates = append ( listOfUpdates , proc )
}
channelHasError := false
for _ , chanRet := range listOfUpdates {
locError := <- chanRet
if locError != nil {
channelHasError = true
}
}
if channelHasError {
2020-04-11 00:13:31 -05:00
errRt := errors . New ( 500 , "there was an error updating the groups" )
2020-04-08 19:38:18 -05:00
return nil , errRt
}
userInfo , err := getUserInfo ( ctx , client , user )
if err != nil {
return nil , err
}
2021-05-08 00:00:29 -07:00
policies := strings . Split ( userInfo . PolicyName , "," )
2020-04-08 19:38:18 -05:00
userReturn := & models . User {
AccessKey : user ,
MemberOf : userInfo . MemberOf ,
2021-05-08 00:00:29 -07:00
Policy : policies ,
2020-04-08 19:38:18 -05:00
Status : string ( userInfo . Status ) ,
}
return userReturn , nil
}
2022-04-27 11:45:04 -07:00
func getUpdateUserGroupsResponse ( session * models . Principal , params userApi . UpdateUserGroupsParams ) ( * models . User , * models . Error ) {
2022-04-28 12:55:06 -07:00
ctx , cancel := context . WithCancel ( params . HTTPRequest . Context ( ) )
2022-04-11 16:20:30 -07:00
defer cancel ( )
2020-04-08 19:38:18 -05:00
2021-07-19 11:48:50 -07:00
mAdmin , err := NewMinioAdminClient ( session )
2020-04-08 19:38:18 -05:00
if err != nil {
2022-04-28 12:55:06 -07:00
return nil , ErrorWithContext ( ctx , err )
2020-04-08 19:38:18 -05:00
}
// create a minioClient interface implementation
// defining the client to be used
2021-07-19 11:48:50 -07:00
adminClient := AdminClient { Client : mAdmin }
2020-04-08 19:38:18 -05:00
2022-05-13 13:13:56 -07:00
userName , err := utils . DecodeBase64 ( params . Name )
if err != nil {
return nil , ErrorWithContext ( ctx , err )
}
user , err := updateUserGroups ( ctx , adminClient , userName , params . Body . Groups )
2020-04-08 19:38:18 -05:00
if err != nil {
2022-04-28 12:55:06 -07:00
return nil , ErrorWithContext ( ctx , err )
2020-04-08 19:38:18 -05:00
}
return user , nil
}
2020-04-11 00:13:31 -05:00
// setUserStatus invokes setUserStatus from madmin to update user status
func setUserStatus ( ctx context . Context , client MinioAdmin , user string , status string ) error {
var setStatus madmin . AccountStatus
switch status {
case "enabled" :
setStatus = madmin . AccountEnabled
case "disabled" :
setStatus = madmin . AccountDisabled
default :
return errors . New ( 500 , "status not valid" )
}
2021-06-07 20:53:03 -07:00
return client . setUserStatus ( ctx , user , setStatus )
2020-04-11 00:13:31 -05:00
}
2022-04-27 11:45:04 -07:00
func getUpdateUserResponse ( session * models . Principal , params userApi . UpdateUserInfoParams ) ( * models . User , * models . Error ) {
2022-04-28 12:55:06 -07:00
ctx , cancel := context . WithCancel ( params . HTTPRequest . Context ( ) )
2022-04-11 16:20:30 -07:00
defer cancel ( )
2020-04-11 00:13:31 -05:00
2021-07-19 11:48:50 -07:00
mAdmin , err := NewMinioAdminClient ( session )
2020-04-11 00:13:31 -05:00
if err != nil {
2022-04-28 12:55:06 -07:00
return nil , ErrorWithContext ( ctx , err )
2020-04-11 00:13:31 -05:00
}
// create a minioClient interface implementation
// defining the client to be used
2021-07-19 11:48:50 -07:00
adminClient := AdminClient { Client : mAdmin }
2022-05-13 13:13:56 -07:00
userName , err := utils . DecodeBase64 ( params . Name )
if err != nil {
return nil , ErrorWithContext ( ctx , err )
}
2020-04-11 00:13:31 -05:00
status := * params . Body . Status
groups := params . Body . Groups
2022-05-13 13:13:56 -07:00
if err := setUserStatus ( ctx , adminClient , userName , status ) ; err != nil {
2022-04-28 12:55:06 -07:00
return nil , ErrorWithContext ( ctx , err )
2020-04-11 00:13:31 -05:00
}
2022-05-13 13:13:56 -07:00
userElem , errUG := updateUserGroups ( ctx , adminClient , userName , groups )
2020-04-11 00:13:31 -05:00
if errUG != nil {
2022-04-28 12:55:06 -07:00
return nil , ErrorWithContext ( ctx , errUG )
2020-04-11 00:13:31 -05:00
}
return userElem , nil
}
2020-04-15 20:08:35 -05:00
// addUsersListToGroups iterates over the user list & assigns the requested groups to each user.
func addUsersListToGroups ( ctx context . Context , client MinioAdmin , usersToUpdate [ ] string , groupsToAssign [ ] string ) error {
// We update each group with the complete usersList
parallelGroupsUpdate := func ( groupToAssign string ) chan error {
groupProcess := make ( chan error )
go func ( ) {
defer close ( groupProcess )
// We add the users array to the group.
err := updateGroupMembers ( ctx , client , groupToAssign , usersToUpdate , false )
groupProcess <- err
} ( )
return groupProcess
}
var groupsUpdateList [ ] chan error
// We get each group name & add users accordingly
for _ , groupName := range groupsToAssign {
// We update the group
proc := parallelGroupsUpdate ( groupName )
groupsUpdateList = append ( groupsUpdateList , proc )
}
errorsList := [ ] string { } // We get the errors list because we want to have all errors at once.
for _ , err := range groupsUpdateList {
2022-04-28 12:55:06 -07:00
errorFromUpdate := <- err // We store the errors to avoid Data Race
2020-04-15 20:08:35 -05:00
if errorFromUpdate != nil {
2022-04-28 12:55:06 -07:00
// If there is an errors, we store the errors strings so we can join them after we receive all errors
2020-04-15 20:08:35 -05:00
errorsList = append ( errorsList , errorFromUpdate . Error ( ) ) // We wait until all the channels have been closed.
}
}
2022-04-28 12:55:06 -07:00
// If there are errors, we throw the final errors with the errors inside
2020-04-15 20:08:35 -05:00
if len ( errorsList ) > 0 {
2022-05-05 13:44:10 -07:00
errGen := fmt . Errorf ( "error in users-groups assignation: %q" , strings . Join ( errorsList , "," ) )
2020-04-15 20:08:35 -05:00
return errGen
}
return nil
}
2022-04-27 11:45:04 -07:00
func getAddUsersListToGroupsResponse ( session * models . Principal , params userApi . BulkUpdateUsersGroupsParams ) * models . Error {
2022-04-28 12:55:06 -07:00
ctx , cancel := context . WithCancel ( params . HTTPRequest . Context ( ) )
2022-04-11 16:20:30 -07:00
defer cancel ( )
2020-04-15 20:08:35 -05:00
2021-07-19 11:48:50 -07:00
mAdmin , err := NewMinioAdminClient ( session )
2020-04-15 20:08:35 -05:00
if err != nil {
2022-04-28 12:55:06 -07:00
return ErrorWithContext ( ctx , err )
2020-04-15 20:08:35 -05:00
}
// create a minioClient interface implementation
// defining the client to be used
2021-07-19 11:48:50 -07:00
adminClient := AdminClient { Client : mAdmin }
2020-04-15 20:08:35 -05:00
usersList := params . Body . Users
groupsList := params . Body . Groups
if err := addUsersListToGroups ( ctx , adminClient , usersList , groupsList ) ; err != nil {
2022-04-28 12:55:06 -07:00
return ErrorWithContext ( ctx , err )
2020-04-15 20:08:35 -05:00
}
return nil
}
2021-04-02 16:13:29 -07:00
2022-04-28 12:55:06 -07:00
func getListUsersWithAccessToBucketResponse ( session * models . Principal , params bucketApi . ListUsersWithAccessToBucketParams ) ( [ ] string , * models . Error ) {
ctx , cancel := context . WithCancel ( params . HTTPRequest . Context ( ) )
2022-04-11 16:20:30 -07:00
defer cancel ( )
2021-07-19 11:48:50 -07:00
mAdmin , err := NewMinioAdminClient ( session )
2021-04-02 16:13:29 -07:00
if err != nil {
2022-04-28 12:55:06 -07:00
return nil , ErrorWithContext ( ctx , err )
2021-04-02 16:13:29 -07:00
}
// create a minioClient interface implementation
// defining the client to be used
2021-07-19 11:48:50 -07:00
adminClient := AdminClient { Client : mAdmin }
2022-04-28 12:55:06 -07:00
list , err := listUsersWithAccessToBucket ( ctx , adminClient , params . Bucket )
if err != nil {
return nil , ErrorWithContext ( ctx , err )
}
return list , nil
2021-05-28 18:28:39 -07:00
}
func policyAllowsAndMatchesBucket ( policy * iampolicy . Policy , bucket string ) int {
policyStatements := policy . Statements
for i := 0 ; i < len ( policyStatements ) ; i ++ {
resources := policyStatements [ i ] . Resources
effect := policyStatements [ i ] . Effect
if resources . Match ( bucket , map [ string ] [ ] string { } ) {
if effect . IsValid ( ) {
if effect . IsAllowed ( true ) {
return Allow
}
return Deny
}
}
}
return Unknown
}
2022-04-28 12:55:06 -07:00
func listUsersWithAccessToBucket ( ctx context . Context , adminClient MinioAdmin , bucket string ) ( [ ] string , error ) {
2021-05-28 18:28:39 -07:00
users , err := adminClient . listUsers ( ctx )
2021-04-02 16:13:29 -07:00
if err != nil {
2022-04-28 12:55:06 -07:00
return nil , err
2021-04-02 16:13:29 -07:00
}
var retval [ ] string
2021-06-04 11:35:55 -07:00
akHasAccess := make ( map [ string ] struct { } )
akIsDenied := make ( map [ string ] struct { } )
2021-05-28 18:28:39 -07:00
for k , v := range users {
for _ , policyName := range strings . Split ( v . PolicyName , "," ) {
policyName = strings . TrimSpace ( policyName )
2021-06-04 11:35:55 -07:00
if policyName == "" {
continue
}
2021-05-08 00:00:29 -07:00
policy , err := adminClient . getPolicy ( ctx , policyName )
2021-06-04 11:35:55 -07:00
if err != nil {
2022-04-28 12:55:06 -07:00
ErrorWithContext ( ctx , fmt . Errorf ( "unable to fetch policy %s: %v" , policyName , err ) )
2021-06-04 11:35:55 -07:00
continue
}
if _ , ok := akIsDenied [ k ] ; ! ok {
switch policyAllowsAndMatchesBucket ( policy , bucket ) {
case Allow :
if _ , ok := akHasAccess [ k ] ; ! ok {
akHasAccess [ k ] = struct { } { }
2021-05-28 18:28:39 -07:00
}
2021-06-04 11:35:55 -07:00
case Deny :
akIsDenied [ k ] = struct { } { }
delete ( akHasAccess , k )
2021-05-08 00:00:29 -07:00
}
2021-04-02 16:13:29 -07:00
}
}
}
2021-06-04 11:35:55 -07:00
groups , err := adminClient . listGroups ( ctx )
2021-04-02 16:13:29 -07:00
if err != nil {
2022-04-28 12:55:06 -07:00
ErrorWithContext ( ctx , fmt . Errorf ( "unable to list groups: %v" , err ) )
2021-04-02 16:13:29 -07:00
return retval , nil
}
2021-06-04 11:35:55 -07:00
for _ , groupName := range groups {
info , err := groupInfo ( ctx , adminClient , groupName )
if err != nil {
2022-04-28 12:55:06 -07:00
ErrorWithContext ( ctx , fmt . Errorf ( "unable to fetch group info %s: %v" , groupName , err ) )
2021-06-04 11:35:55 -07:00
continue
}
policy , err := adminClient . getPolicy ( ctx , info . Policy )
if err != nil {
2022-04-28 12:55:06 -07:00
ErrorWithContext ( ctx , fmt . Errorf ( "unable to fetch group policy %s: %v" , info . Policy , err ) )
2021-06-04 11:35:55 -07:00
continue
}
for _ , member := range info . Members {
if _ , ok := akIsDenied [ member ] ; ! ok {
switch policyAllowsAndMatchesBucket ( policy , bucket ) {
case Allow :
if _ , ok := akHasAccess [ member ] ; ! ok {
akHasAccess [ member ] = struct { } { }
2021-04-02 16:13:29 -07:00
}
2021-06-04 11:35:55 -07:00
case Deny :
akIsDenied [ member ] = struct { } { }
delete ( akHasAccess , member )
2021-04-02 16:13:29 -07:00
}
}
}
}
2021-06-04 11:35:55 -07:00
for k := range akHasAccess {
retval = append ( retval , k )
2021-05-28 18:28:39 -07:00
}
sort . Strings ( retval )
2021-04-02 16:13:29 -07:00
return retval , nil
}
2021-06-03 15:32:22 -07:00
// changeUserPassword changes password of selectedUser to newSecretKey
func changeUserPassword ( ctx context . Context , client MinioAdmin , selectedUser string , newSecretKey string ) error {
2021-06-07 20:53:03 -07:00
return client . changePassword ( ctx , selectedUser , newSecretKey )
2021-06-03 15:32:22 -07:00
}
// getChangeUserPasswordResponse will change the password of selctedUser to newSecretKey
2022-04-27 11:45:04 -07:00
func getChangeUserPasswordResponse ( session * models . Principal , params accountApi . ChangeUserPasswordParams ) * models . Error {
2022-04-28 12:55:06 -07:00
ctx , cancel := context . WithCancel ( params . HTTPRequest . Context ( ) )
2022-04-11 16:20:30 -07:00
defer cancel ( )
2021-07-19 11:48:50 -07:00
mAdmin , err := NewMinioAdminClient ( session )
2021-06-03 15:32:22 -07:00
if err != nil {
2022-04-28 12:55:06 -07:00
return ErrorWithContext ( ctx , err )
2021-06-03 15:32:22 -07:00
}
// create a minioClient interface implementation
// defining the client to be used
2021-07-19 11:48:50 -07:00
adminClient := AdminClient { Client : mAdmin }
2021-06-03 15:32:22 -07:00
// params will contain selectedUser and newSecretKey credentials for the user
user := * params . Body . SelectedUser
newSecretKey := * params . Body . NewSecretKey
// changes password of user to newSecretKey
if err := changeUserPassword ( ctx , adminClient , user , newSecretKey ) ; err != nil {
2022-04-28 12:55:06 -07:00
return ErrorWithContext ( ctx , err )
2021-06-03 15:32:22 -07:00
}
return nil
}
2022-05-22 22:14:24 -07:00
func getCheckUserSAResponse ( session * models . Principal , params userApi . CheckUserServiceAccountsParams ) ( * models . UserServiceAccountSummary , * models . Error ) {
ctx , cancel := context . WithCancel ( params . HTTPRequest . Context ( ) )
defer cancel ( )
mAdmin , err := NewMinioAdminClient ( session )
if err != nil {
return nil , ErrorWithContext ( ctx , err )
}
// create a minioClient interface implementation
// defining the client to be used
adminClient := AdminClient { Client : mAdmin }
var userServiceAccountList [ ] * models . UserServiceAccountItem
hasSA := false
for _ , user := range params . SelectedUsers {
listServAccs , err := adminClient . listServiceAccounts ( ctx , user )
if err != nil {
return nil , ErrorWithContext ( ctx , err )
}
numSAs := int64 ( len ( listServAccs . Accounts ) )
if numSAs > 0 {
hasSA = true
}
userAccountItem := & models . UserServiceAccountItem {
UserName : user ,
NumSAs : numSAs ,
}
userServiceAccountList = append ( userServiceAccountList , userAccountItem )
}
userAccountList := & models . UserServiceAccountSummary {
UserServiceAccountList : userServiceAccountList ,
HasSA : hasSA ,
}
return userAccountList , nil
}