github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/admin-handler-utils.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package cmd 19 20 import ( 21 "context" 22 "errors" 23 "fmt" 24 "net/http" 25 26 "github.com/minio/kms-go/kes" 27 "github.com/minio/madmin-go/v3" 28 "github.com/minio/minio/internal/auth" 29 "github.com/minio/minio/internal/config" 30 "github.com/minio/pkg/v2/policy" 31 ) 32 33 // validateAdminReq will validate request against and return whether it is allowed. 34 // If any of the supplied actions are allowed it will be successful. 35 // If nil ObjectLayer is returned, the operation is not permitted. 36 // When nil ObjectLayer has been returned an error has always been sent to w. 37 func validateAdminReq(ctx context.Context, w http.ResponseWriter, r *http.Request, actions ...policy.AdminAction) (ObjectLayer, auth.Credentials) { 38 // Get current object layer instance. 39 objectAPI := newObjectLayerFn() 40 if objectAPI == nil || globalNotificationSys == nil { 41 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) 42 return nil, auth.Credentials{} 43 } 44 45 for _, action := range actions { 46 // Validate request signature. 47 cred, adminAPIErr := checkAdminRequestAuth(ctx, r, action, "") 48 switch adminAPIErr { 49 case ErrNone: 50 return objectAPI, cred 51 case ErrAccessDenied: 52 // Try another 53 continue 54 default: 55 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(adminAPIErr), r.URL) 56 return nil, cred 57 } 58 } 59 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL) 60 return nil, auth.Credentials{} 61 } 62 63 // AdminError - is a generic error for all admin APIs. 64 type AdminError struct { 65 Code string 66 Message string 67 StatusCode int 68 } 69 70 func (ae AdminError) Error() string { 71 return ae.Message 72 } 73 74 func toAdminAPIErr(ctx context.Context, err error) APIError { 75 if err == nil { 76 return noError 77 } 78 79 var apiErr APIError 80 switch e := err.(type) { 81 case policy.Error: 82 apiErr = APIError{ 83 Code: "XMinioMalformedIAMPolicy", 84 Description: e.Error(), 85 HTTPStatusCode: http.StatusBadRequest, 86 } 87 case config.ErrConfigNotFound: 88 apiErr = APIError{ 89 Code: "XMinioConfigNotFoundError", 90 Description: e.Error(), 91 HTTPStatusCode: http.StatusNotFound, 92 } 93 case config.ErrConfigGeneric: 94 apiErr = APIError{ 95 Code: "XMinioConfigError", 96 Description: e.Error(), 97 HTTPStatusCode: http.StatusBadRequest, 98 } 99 case AdminError: 100 apiErr = APIError{ 101 Code: e.Code, 102 Description: e.Message, 103 HTTPStatusCode: e.StatusCode, 104 } 105 case SRError: 106 apiErr = errorCodes.ToAPIErrWithErr(e.Code, e.Cause) 107 case decomError: 108 apiErr = APIError{ 109 Code: "XMinioDecommissionNotAllowed", 110 Description: e.Err, 111 HTTPStatusCode: http.StatusBadRequest, 112 } 113 default: 114 switch { 115 case errors.Is(err, errTooManyPolicies): 116 apiErr = APIError{ 117 Code: "XMinioAdminInvalidRequest", 118 Description: err.Error(), 119 HTTPStatusCode: http.StatusBadRequest, 120 } 121 case errors.Is(err, errDecommissionAlreadyRunning): 122 apiErr = APIError{ 123 Code: "XMinioDecommissionNotAllowed", 124 Description: err.Error(), 125 HTTPStatusCode: http.StatusBadRequest, 126 } 127 case errors.Is(err, errDecommissionComplete): 128 apiErr = APIError{ 129 Code: "XMinioDecommissionNotAllowed", 130 Description: err.Error(), 131 HTTPStatusCode: http.StatusBadRequest, 132 } 133 case errors.Is(err, errDecommissionRebalanceAlreadyRunning): 134 apiErr = APIError{ 135 Code: "XMinioDecommissionNotAllowed", 136 Description: err.Error(), 137 HTTPStatusCode: http.StatusBadRequest, 138 } 139 case errors.Is(err, errRebalanceDecommissionAlreadyRunning): 140 apiErr = APIError{ 141 Code: "XMinioRebalanceNotAllowed", 142 Description: err.Error(), 143 HTTPStatusCode: http.StatusBadRequest, 144 } 145 case errors.Is(err, errConfigNotFound): 146 apiErr = APIError{ 147 Code: "XMinioConfigError", 148 Description: err.Error(), 149 HTTPStatusCode: http.StatusNotFound, 150 } 151 case errors.Is(err, errIAMActionNotAllowed): 152 apiErr = APIError{ 153 Code: "XMinioIAMActionNotAllowed", 154 Description: err.Error(), 155 HTTPStatusCode: http.StatusForbidden, 156 } 157 case errors.Is(err, errIAMServiceAccountNotAllowed): 158 apiErr = APIError{ 159 Code: "XMinioIAMServiceAccountNotAllowed", 160 Description: err.Error(), 161 HTTPStatusCode: http.StatusBadRequest, 162 } 163 case errors.Is(err, errIAMNotInitialized): 164 apiErr = APIError{ 165 Code: "XMinioIAMNotInitialized", 166 Description: err.Error(), 167 HTTPStatusCode: http.StatusServiceUnavailable, 168 } 169 case errors.Is(err, errPolicyInUse): 170 apiErr = APIError{ 171 Code: "XMinioIAMPolicyInUse", 172 Description: "The policy cannot be removed, as it is in use", 173 HTTPStatusCode: http.StatusBadRequest, 174 } 175 case errors.Is(err, errSessionPolicyTooLarge): 176 apiErr = APIError{ 177 Code: "XMinioIAMServiceAccountSessionPolicyTooLarge", 178 Description: err.Error(), 179 HTTPStatusCode: http.StatusBadRequest, 180 } 181 case errors.Is(err, kes.ErrKeyExists): 182 apiErr = APIError{ 183 Code: "XMinioKMSKeyExists", 184 Description: err.Error(), 185 HTTPStatusCode: http.StatusConflict, 186 } 187 188 // Tier admin API errors 189 case errors.Is(err, madmin.ErrTierNameEmpty): 190 apiErr = APIError{ 191 Code: "XMinioAdminTierNameEmpty", 192 Description: err.Error(), 193 HTTPStatusCode: http.StatusBadRequest, 194 } 195 case errors.Is(err, madmin.ErrTierInvalidConfig): 196 apiErr = APIError{ 197 Code: "XMinioAdminTierInvalidConfig", 198 Description: err.Error(), 199 HTTPStatusCode: http.StatusBadRequest, 200 } 201 case errors.Is(err, madmin.ErrTierInvalidConfigVersion): 202 apiErr = APIError{ 203 Code: "XMinioAdminTierInvalidConfigVersion", 204 Description: err.Error(), 205 HTTPStatusCode: http.StatusBadRequest, 206 } 207 case errors.Is(err, madmin.ErrTierTypeUnsupported): 208 apiErr = APIError{ 209 Code: "XMinioAdminTierTypeUnsupported", 210 Description: err.Error(), 211 HTTPStatusCode: http.StatusBadRequest, 212 } 213 case errIsTierPermError(err): 214 apiErr = APIError{ 215 Code: "XMinioAdminTierInsufficientPermissions", 216 Description: err.Error(), 217 HTTPStatusCode: http.StatusBadRequest, 218 } 219 default: 220 apiErr = errorCodes.ToAPIErrWithErr(toAdminAPIErrCode(ctx, err), err) 221 } 222 } 223 return apiErr 224 } 225 226 // toAdminAPIErrCode - converts errErasureWriteQuorum error to admin API 227 // specific error. 228 func toAdminAPIErrCode(ctx context.Context, err error) APIErrorCode { 229 if errors.Is(err, errErasureWriteQuorum) { 230 return ErrAdminConfigNoQuorum 231 } 232 return toAPIErrorCode(ctx, err) 233 } 234 235 // wraps export error for more context 236 func exportError(ctx context.Context, err error, fname, entity string) APIError { 237 if entity == "" { 238 return toAPIError(ctx, fmt.Errorf("error exporting %s with: %w", fname, err)) 239 } 240 return toAPIError(ctx, fmt.Errorf("error exporting %s from %s with: %w", entity, fname, err)) 241 } 242 243 // wraps import error for more context 244 func importError(ctx context.Context, err error, fname, entity string) APIError { 245 if entity == "" { 246 return toAPIError(ctx, fmt.Errorf("error importing %s with: %w", fname, err)) 247 } 248 return toAPIError(ctx, fmt.Errorf("error importing %s from %s with: %w", entity, fname, err)) 249 } 250 251 // wraps import error for more context 252 func importErrorWithAPIErr(ctx context.Context, apiErr APIErrorCode, err error, fname, entity string) APIError { 253 if entity == "" { 254 return errorCodes.ToAPIErrWithErr(apiErr, fmt.Errorf("error importing %s with: %w", fname, err)) 255 } 256 return errorCodes.ToAPIErrWithErr(apiErr, fmt.Errorf("error importing %s from %s with: %w", entity, fname, err)) 257 }