github.com/minio/console@v1.4.1/api/user_account.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 "net/http" 22 23 authApi "github.com/minio/console/api/operations/auth" 24 25 "github.com/minio/console/pkg/auth" 26 27 "github.com/go-openapi/runtime" 28 "github.com/go-openapi/runtime/middleware" 29 "github.com/minio/console/api/operations" 30 accountApi "github.com/minio/console/api/operations/account" 31 "github.com/minio/console/models" 32 ) 33 34 func registerAccountHandlers(api *operations.ConsoleAPI) { 35 // change user password 36 api.AccountAccountChangePasswordHandler = accountApi.AccountChangePasswordHandlerFunc(func(params accountApi.AccountChangePasswordParams, session *models.Principal) middleware.Responder { 37 changePasswordResponse, err := getChangePasswordResponse(session, params) 38 if err != nil { 39 return accountApi.NewAccountChangePasswordDefault(err.Code).WithPayload(err.APIError) 40 } 41 // Custom response writer to update the session cookies 42 return middleware.ResponderFunc(func(w http.ResponseWriter, p runtime.Producer) { 43 cookie := NewSessionCookieForConsole(changePasswordResponse.SessionID) 44 http.SetCookie(w, &cookie) 45 authApi.NewLoginNoContent().WriteResponse(w, p) 46 }) 47 }) 48 } 49 50 // changePassword validate current current user password and if it's correct set the new password 51 func changePassword(ctx context.Context, client MinioAdmin, session *models.Principal, newSecretKey string) error { 52 return client.changePassword(ctx, session.AccountAccessKey, newSecretKey) 53 } 54 55 // getChangePasswordResponse will validate user knows what is the current password (avoid account hijacking), update user account password 56 // and authenticate the user generating a new session token/cookie 57 func getChangePasswordResponse(session *models.Principal, params accountApi.AccountChangePasswordParams) (*models.LoginResponse, *CodedAPIError) { 58 ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) 59 defer cancel() 60 clientIP := getClientIP(params.HTTPRequest) 61 62 // changePassword operations requires an AdminClient initialized with parent account credentials not 63 // STS credentials 64 parentAccountClient, err := NewMinioAdminClient(params.HTTPRequest.Context(), &models.Principal{ 65 STSAccessKeyID: session.AccountAccessKey, 66 STSSecretAccessKey: *params.Body.CurrentSecretKey, 67 }) 68 if err != nil { 69 return nil, ErrorWithContext(ctx, err) 70 } 71 // parentAccountClient will contain access and secret key credentials for the user 72 userClient := AdminClient{Client: parentAccountClient} 73 accessKey := session.AccountAccessKey 74 newSecretKey := *params.Body.NewSecretKey 75 76 // currentSecretKey will compare currentSecretKey against the stored secret key inside the encrypted session 77 if err := changePassword(ctx, userClient, session, newSecretKey); err != nil { 78 return nil, ErrorWithContext(ctx, ErrChangePassword, nil, err) 79 } 80 // user credentials are updated at this point, we need to generate a new admin client and authenticate using 81 // the new credentials 82 credentials, err := getConsoleCredentials(accessKey, newSecretKey, clientIP) 83 if err != nil { 84 return nil, ErrorWithContext(ctx, ErrInvalidLogin, nil, err) 85 } 86 // authenticate user and generate new session token 87 sessionID, err := login(credentials, &auth.SessionFeatures{HideMenu: session.Hm}) 88 if err != nil { 89 return nil, ErrorWithContext(ctx, ErrInvalidLogin, nil, err) 90 } 91 // serialize output 92 loginResponse := &models.LoginResponse{ 93 SessionID: *sessionID, 94 } 95 return loginResponse, nil 96 }