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  }