| 
									
										
										
										
											2021-08-10 09:14:38 +08:00
										 |  |  | // Copyright (c) 2015-2021 MinIO, Inc.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This file is part of MinIO Object Storage stack
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 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 cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							|  |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2021-08-10 09:14:38 +08:00
										 |  |  | 	"net/http" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-02 05:09:42 +08:00
										 |  |  | 	"github.com/minio/kms-go/kes" | 
					
						
							| 
									
										
										
										
											2023-06-20 08:53:08 +08:00
										 |  |  | 	"github.com/minio/madmin-go/v3" | 
					
						
							| 
									
										
										
										
											2021-08-10 09:14:38 +08:00
										 |  |  | 	"github.com/minio/minio/internal/auth" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/config" | 
					
						
							| 
									
										
										
										
											2023-09-15 05:50:16 +08:00
										 |  |  | 	"github.com/minio/pkg/v2/policy" | 
					
						
							| 
									
										
										
										
											2021-08-10 09:14:38 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-22 01:26:59 +08:00
										 |  |  | // validateAdminReq will validate request against and return whether it is allowed.
 | 
					
						
							|  |  |  | // If any of the supplied actions are allowed it will be successful.
 | 
					
						
							|  |  |  | // If nil ObjectLayer is returned, the operation is not permitted.
 | 
					
						
							|  |  |  | // When nil ObjectLayer has been returned an error has always been sent to w.
 | 
					
						
							| 
									
										
										
										
											2023-09-15 05:50:16 +08:00
										 |  |  | func validateAdminReq(ctx context.Context, w http.ResponseWriter, r *http.Request, actions ...policy.AdminAction) (ObjectLayer, auth.Credentials) { | 
					
						
							| 
									
										
										
										
											2021-08-10 09:14:38 +08:00
										 |  |  | 	// Get current object layer instance.
 | 
					
						
							|  |  |  | 	objectAPI := newObjectLayerFn() | 
					
						
							|  |  |  | 	if objectAPI == nil || globalNotificationSys == nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) | 
					
						
							|  |  |  | 		return nil, auth.Credentials{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-24 04:02:16 +08:00
										 |  |  | 	for _, action := range actions { | 
					
						
							|  |  |  | 		// Validate request signature.
 | 
					
						
							|  |  |  | 		cred, adminAPIErr := checkAdminRequestAuth(ctx, r, action, "") | 
					
						
							| 
									
										
										
										
											2022-07-22 01:26:59 +08:00
										 |  |  | 		switch adminAPIErr { | 
					
						
							|  |  |  | 		case ErrNone: | 
					
						
							|  |  |  | 			return objectAPI, cred | 
					
						
							|  |  |  | 		case ErrAccessDenied: | 
					
						
							|  |  |  | 			// Try another
 | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		default: | 
					
						
							| 
									
										
										
										
											2021-11-24 04:02:16 +08:00
										 |  |  | 			writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(adminAPIErr), r.URL) | 
					
						
							|  |  |  | 			return nil, cred | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-08-10 09:14:38 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-11-24 04:02:16 +08:00
										 |  |  | 	writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL) | 
					
						
							|  |  |  | 	return nil, auth.Credentials{} | 
					
						
							| 
									
										
										
										
											2021-08-10 09:14:38 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // AdminError - is a generic error for all admin APIs.
 | 
					
						
							|  |  |  | type AdminError struct { | 
					
						
							|  |  |  | 	Code       string | 
					
						
							|  |  |  | 	Message    string | 
					
						
							|  |  |  | 	StatusCode int | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (ae AdminError) Error() string { | 
					
						
							|  |  |  | 	return ae.Message | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func toAdminAPIErr(ctx context.Context, err error) APIError { | 
					
						
							|  |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		return noError | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var apiErr APIError | 
					
						
							|  |  |  | 	switch e := err.(type) { | 
					
						
							| 
									
										
										
										
											2023-09-15 05:50:16 +08:00
										 |  |  | 	case policy.Error: | 
					
						
							| 
									
										
										
										
											2021-08-10 09:14:38 +08:00
										 |  |  | 		apiErr = APIError{ | 
					
						
							|  |  |  | 			Code:           "XMinioMalformedIAMPolicy", | 
					
						
							|  |  |  | 			Description:    e.Error(), | 
					
						
							|  |  |  | 			HTTPStatusCode: http.StatusBadRequest, | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-11-19 02:28:14 +08:00
										 |  |  | 	case config.ErrConfigNotFound: | 
					
						
							|  |  |  | 		apiErr = APIError{ | 
					
						
							|  |  |  | 			Code:           "XMinioConfigNotFoundError", | 
					
						
							|  |  |  | 			Description:    e.Error(), | 
					
						
							|  |  |  | 			HTTPStatusCode: http.StatusNotFound, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	case config.ErrConfigGeneric: | 
					
						
							| 
									
										
										
										
											2021-08-10 09:14:38 +08:00
										 |  |  | 		apiErr = APIError{ | 
					
						
							|  |  |  | 			Code:           "XMinioConfigError", | 
					
						
							|  |  |  | 			Description:    e.Error(), | 
					
						
							|  |  |  | 			HTTPStatusCode: http.StatusBadRequest, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	case AdminError: | 
					
						
							|  |  |  | 		apiErr = APIError{ | 
					
						
							|  |  |  | 			Code:           e.Code, | 
					
						
							|  |  |  | 			Description:    e.Message, | 
					
						
							|  |  |  | 			HTTPStatusCode: e.StatusCode, | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-12-15 06:09:57 +08:00
										 |  |  | 	case SRError: | 
					
						
							|  |  |  | 		apiErr = errorCodes.ToAPIErrWithErr(e.Code, e.Cause) | 
					
						
							| 
									
										
										
										
											2022-01-12 10:48:43 +08:00
										 |  |  | 	case decomError: | 
					
						
							|  |  |  | 		apiErr = APIError{ | 
					
						
							|  |  |  | 			Code:           "XMinioDecommissionNotAllowed", | 
					
						
							|  |  |  | 			Description:    e.Err, | 
					
						
							|  |  |  | 			HTTPStatusCode: http.StatusBadRequest, | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-08-10 09:14:38 +08:00
										 |  |  | 	default: | 
					
						
							|  |  |  | 		switch { | 
					
						
							| 
									
										
										
										
											2022-06-21 01:48:11 +08:00
										 |  |  | 		case errors.Is(err, errTooManyPolicies): | 
					
						
							|  |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioAdminInvalidRequest", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusBadRequest, | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-01-11 09:26:00 +08:00
										 |  |  | 		case errors.Is(err, errDecommissionAlreadyRunning): | 
					
						
							|  |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioDecommissionNotAllowed", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusBadRequest, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case errors.Is(err, errDecommissionComplete): | 
					
						
							|  |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioDecommissionNotAllowed", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusBadRequest, | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-10-26 03:36:57 +08:00
										 |  |  | 		case errors.Is(err, errDecommissionRebalanceAlreadyRunning): | 
					
						
							|  |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioDecommissionNotAllowed", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusBadRequest, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case errors.Is(err, errRebalanceDecommissionAlreadyRunning): | 
					
						
							|  |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioRebalanceNotAllowed", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusBadRequest, | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-08-10 09:14:38 +08:00
										 |  |  | 		case errors.Is(err, errConfigNotFound): | 
					
						
							|  |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioConfigError", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusNotFound, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case errors.Is(err, errIAMActionNotAllowed): | 
					
						
							|  |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioIAMActionNotAllowed", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusForbidden, | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-01-26 00:20:12 +08:00
										 |  |  | 		case errors.Is(err, errIAMServiceAccountNotAllowed): | 
					
						
							| 
									
										
										
										
											2022-01-11 06:26:26 +08:00
										 |  |  | 			apiErr = APIError{ | 
					
						
							| 
									
										
										
										
											2023-01-26 00:20:12 +08:00
										 |  |  | 				Code:           "XMinioIAMServiceAccountNotAllowed", | 
					
						
							| 
									
										
										
										
											2022-01-11 06:26:26 +08:00
										 |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusBadRequest, | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-08-10 09:14:38 +08:00
										 |  |  | 		case errors.Is(err, errIAMNotInitialized): | 
					
						
							|  |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioIAMNotInitialized", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusServiceUnavailable, | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-11-04 10:47:49 +08:00
										 |  |  | 		case errors.Is(err, errPolicyInUse): | 
					
						
							|  |  |  | 			apiErr = APIError{ | 
					
						
							| 
									
										
										
										
											2023-05-09 15:53:08 +08:00
										 |  |  | 				Code:           "XMinioIAMPolicyInUse", | 
					
						
							| 
									
										
										
										
											2021-11-04 10:47:49 +08:00
										 |  |  | 				Description:    "The policy cannot be removed, as it is in use", | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusBadRequest, | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-05-09 15:53:08 +08:00
										 |  |  | 		case errors.Is(err, errSessionPolicyTooLarge): | 
					
						
							|  |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioIAMServiceAccountSessionPolicyTooLarge", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusBadRequest, | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-03-04 01:42:37 +08:00
										 |  |  | 		case errors.Is(err, kes.ErrKeyExists): | 
					
						
							| 
									
										
										
										
											2021-08-10 09:14:38 +08:00
										 |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioKMSKeyExists", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusConflict, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Tier admin API errors
 | 
					
						
							|  |  |  | 		case errors.Is(err, madmin.ErrTierNameEmpty): | 
					
						
							|  |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioAdminTierNameEmpty", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusBadRequest, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case errors.Is(err, madmin.ErrTierInvalidConfig): | 
					
						
							|  |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioAdminTierInvalidConfig", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusBadRequest, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case errors.Is(err, madmin.ErrTierInvalidConfigVersion): | 
					
						
							|  |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioAdminTierInvalidConfigVersion", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusBadRequest, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case errors.Is(err, madmin.ErrTierTypeUnsupported): | 
					
						
							|  |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioAdminTierTypeUnsupported", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusBadRequest, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case errIsTierPermError(err): | 
					
						
							|  |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioAdminTierInsufficientPermissions", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusBadRequest, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			apiErr = errorCodes.ToAPIErrWithErr(toAdminAPIErrCode(ctx, err), err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return apiErr | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // toAdminAPIErrCode - converts errErasureWriteQuorum error to admin API
 | 
					
						
							|  |  |  | // specific error.
 | 
					
						
							|  |  |  | func toAdminAPIErrCode(ctx context.Context, err error) APIErrorCode { | 
					
						
							| 
									
										
										
										
											2023-05-26 00:39:06 +08:00
										 |  |  | 	if errors.Is(err, errErasureWriteQuorum) { | 
					
						
							| 
									
										
										
										
											2021-08-10 09:14:38 +08:00
										 |  |  | 		return ErrAdminConfigNoQuorum | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-05-26 00:39:06 +08:00
										 |  |  | 	return toAPIErrorCode(ctx, err) | 
					
						
							| 
									
										
										
										
											2021-08-10 09:14:38 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // wraps export error for more context
 | 
					
						
							|  |  |  | func exportError(ctx context.Context, err error, fname, entity string) APIError { | 
					
						
							|  |  |  | 	if entity == "" { | 
					
						
							|  |  |  | 		return toAPIError(ctx, fmt.Errorf("error exporting %s with: %w", fname, err)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return toAPIError(ctx, fmt.Errorf("error exporting %s from %s with: %w", entity, fname, err)) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // wraps import error for more context
 | 
					
						
							|  |  |  | func importError(ctx context.Context, err error, fname, entity string) APIError { | 
					
						
							|  |  |  | 	if entity == "" { | 
					
						
							|  |  |  | 		return toAPIError(ctx, fmt.Errorf("error importing %s with: %w", fname, err)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return toAPIError(ctx, fmt.Errorf("error importing %s from %s with: %w", entity, fname, err)) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-06-24 00:25:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // wraps import error for more context
 | 
					
						
							|  |  |  | func importErrorWithAPIErr(ctx context.Context, apiErr APIErrorCode, err error, fname, entity string) APIError { | 
					
						
							|  |  |  | 	if entity == "" { | 
					
						
							|  |  |  | 		return errorCodes.ToAPIErrWithErr(apiErr, fmt.Errorf("error importing %s with: %w", fname, err)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return errorCodes.ToAPIErrWithErr(apiErr, fmt.Errorf("error importing %s from %s with: %w", entity, fname, err)) | 
					
						
							|  |  |  | } |