github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/signature-v2_test.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  	"fmt"
    23  	"net/http"
    24  	"net/url"
    25  	"os"
    26  	"sort"
    27  	"testing"
    28  )
    29  
    30  // Tests for 'func TestResourceListSorting(t *testing.T)'.
    31  func TestResourceListSorting(t *testing.T) {
    32  	sortedResourceList := make([]string, len(resourceList))
    33  	copy(sortedResourceList, resourceList)
    34  	sort.Strings(sortedResourceList)
    35  	for i := 0; i < len(resourceList); i++ {
    36  		if resourceList[i] != sortedResourceList[i] {
    37  			t.Errorf("Expected resourceList[%d] = \"%s\", resourceList is not correctly sorted.", i, sortedResourceList[i])
    38  			break
    39  		}
    40  	}
    41  }
    42  
    43  // Tests presigned v2 signature.
    44  func TestDoesPresignedV2SignatureMatch(t *testing.T) {
    45  	ctx, cancel := context.WithCancel(context.Background())
    46  	defer cancel()
    47  
    48  	obj, fsDir, err := prepareFS(ctx)
    49  	if err != nil {
    50  		t.Fatal(err)
    51  	}
    52  	defer os.RemoveAll(fsDir)
    53  	if err = newTestConfig(globalMinioDefaultRegion, obj); err != nil {
    54  		t.Fatal(err)
    55  	}
    56  
    57  	now := UTCNow()
    58  
    59  	var (
    60  		accessKey = globalActiveCred.AccessKey
    61  		secretKey = globalActiveCred.SecretKey
    62  	)
    63  	testCases := []struct {
    64  		queryParams map[string]string
    65  		expected    APIErrorCode
    66  	}{
    67  		// (0) Should error without a set URL query.
    68  		{
    69  			expected: ErrInvalidQueryParams,
    70  		},
    71  		// (1) Should error on an invalid access key.
    72  		{
    73  			queryParams: map[string]string{
    74  				"Expires":        "60",
    75  				"Signature":      "badsignature",
    76  				"AWSAccessKeyId": "Z7IXGOO6BZ0REAN1Q26I",
    77  			},
    78  			expected: ErrInvalidAccessKeyID,
    79  		},
    80  		// (2) Should error with malformed expires.
    81  		{
    82  			queryParams: map[string]string{
    83  				"Expires":        "60s",
    84  				"Signature":      "badsignature",
    85  				"AWSAccessKeyId": accessKey,
    86  			},
    87  			expected: ErrMalformedExpires,
    88  		},
    89  		// (3) Should give an expired request if it has expired.
    90  		{
    91  			queryParams: map[string]string{
    92  				"Expires":        "60",
    93  				"Signature":      "badsignature",
    94  				"AWSAccessKeyId": accessKey,
    95  			},
    96  			expected: ErrExpiredPresignRequest,
    97  		},
    98  		// (4) Should error when the signature does not match.
    99  		{
   100  			queryParams: map[string]string{
   101  				"Expires":        fmt.Sprintf("%d", now.Unix()+60),
   102  				"Signature":      "badsignature",
   103  				"AWSAccessKeyId": accessKey,
   104  			},
   105  			expected: ErrSignatureDoesNotMatch,
   106  		},
   107  		// (5) Should error when the signature does not match.
   108  		{
   109  			queryParams: map[string]string{
   110  				"Expires":        fmt.Sprintf("%d", now.Unix()+60),
   111  				"Signature":      "zOM2YrY/yAQe15VWmT78OlBrK6g=",
   112  				"AWSAccessKeyId": accessKey,
   113  			},
   114  			expected: ErrSignatureDoesNotMatch,
   115  		},
   116  		// (6) Should not error signature matches with extra query params.
   117  		{
   118  			queryParams: map[string]string{
   119  				"response-content-disposition": "attachment; filename=\"4K%2d4M.txt\"",
   120  			},
   121  			expected: ErrNone,
   122  		},
   123  		// (7) Should not error signature matches with no special query params.
   124  		{
   125  			queryParams: map[string]string{},
   126  			expected:    ErrNone,
   127  		},
   128  	}
   129  
   130  	// Run each test case individually.
   131  	for i, testCase := range testCases {
   132  		// Turn the map[string]string into map[string][]string, because Go.
   133  		query := url.Values{}
   134  		for key, value := range testCase.queryParams {
   135  			query.Set(key, value)
   136  		}
   137  		// Create a request to use.
   138  		req, err := http.NewRequest(http.MethodGet, "http://host/a/b?"+query.Encode(), nil)
   139  		if err != nil {
   140  			t.Errorf("(%d) failed to create http.Request, got %v", i, err)
   141  		}
   142  		if testCase.expected != ErrNone {
   143  			// Should be set since we are simulating a http server.
   144  			req.RequestURI = req.URL.RequestURI()
   145  			// Check if it matches!
   146  			errCode := doesPresignV2SignatureMatch(req)
   147  			if errCode != testCase.expected {
   148  				t.Errorf("(%d) expected to get %s, instead got %s", i, niceError(testCase.expected), niceError(errCode))
   149  			}
   150  		} else {
   151  			err = preSignV2(req, accessKey, secretKey, now.Unix()+60)
   152  			if err != nil {
   153  				t.Fatalf("(%d) failed to preSignV2 http request, got %v", i, err)
   154  			}
   155  			// Should be set since we are simulating a http server.
   156  			req.RequestURI = req.URL.RequestURI()
   157  			errCode := doesPresignV2SignatureMatch(req)
   158  			if errCode != testCase.expected {
   159  				t.Errorf("(%d) expected to get success, instead got %s", i, niceError(errCode))
   160  			}
   161  		}
   162  
   163  	}
   164  }
   165  
   166  // TestValidateV2AuthHeader - Tests validate the logic of V2 Authorization header validator.
   167  func TestValidateV2AuthHeader(t *testing.T) {
   168  	ctx, cancel := context.WithCancel(context.Background())
   169  	defer cancel()
   170  
   171  	obj, fsDir, err := prepareFS(ctx)
   172  	if err != nil {
   173  		t.Fatal(err)
   174  	}
   175  	defer os.RemoveAll(fsDir)
   176  	if err = newTestConfig(globalMinioDefaultRegion, obj); err != nil {
   177  		t.Fatal(err)
   178  	}
   179  
   180  	accessID := globalActiveCred.AccessKey
   181  	testCases := []struct {
   182  		authString    string
   183  		expectedError APIErrorCode
   184  	}{
   185  		// Test case - 1.
   186  		// Case with empty V2AuthString.
   187  		{
   188  			authString:    "",
   189  			expectedError: ErrAuthHeaderEmpty,
   190  		},
   191  		// Test case - 2.
   192  		// Test case with `signV2Algorithm` ("AWS") not being the prefix.
   193  		{
   194  			authString:    "NoV2Prefix",
   195  			expectedError: ErrSignatureVersionNotSupported,
   196  		},
   197  		// Test case - 3.
   198  		// Test case with missing parts in the Auth string.
   199  		// below is the correct format of V2 Authorization header.
   200  		// Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature
   201  		{
   202  			authString:    signV2Algorithm,
   203  			expectedError: ErrMissingFields,
   204  		},
   205  		// Test case - 4.
   206  		// Test case with signature part missing.
   207  		{
   208  			authString:    fmt.Sprintf("%s %s", signV2Algorithm, accessID),
   209  			expectedError: ErrMissingFields,
   210  		},
   211  		// Test case - 5.
   212  		// Test case with wrong accessID.
   213  		{
   214  			authString:    fmt.Sprintf("%s %s:%s", signV2Algorithm, "InvalidAccessID", "signature"),
   215  			expectedError: ErrInvalidAccessKeyID,
   216  		},
   217  		// Test case - 6.
   218  		// Case with right accessID and format.
   219  		{
   220  			authString:    fmt.Sprintf("%s %s:%s", signV2Algorithm, accessID, "signature"),
   221  			expectedError: ErrNone,
   222  		},
   223  	}
   224  
   225  	for i, testCase := range testCases {
   226  		t.Run(fmt.Sprintf("Case %d AuthStr \"%s\".", i+1, testCase.authString), func(t *testing.T) {
   227  			req := &http.Request{
   228  				Header: make(http.Header),
   229  				URL:    &url.URL{},
   230  			}
   231  			req.Header.Set("Authorization", testCase.authString)
   232  			_, actualErrCode := validateV2AuthHeader(req)
   233  
   234  			if testCase.expectedError != actualErrCode {
   235  				t.Errorf("Expected the error code to be %v, got %v.", testCase.expectedError, actualErrCode)
   236  			}
   237  		})
   238  	}
   239  }
   240  
   241  func TestDoesPolicySignatureV2Match(t *testing.T) {
   242  	ctx, cancel := context.WithCancel(context.Background())
   243  	defer cancel()
   244  
   245  	obj, fsDir, err := prepareFS(ctx)
   246  	if err != nil {
   247  		t.Fatal(err)
   248  	}
   249  	defer os.RemoveAll(fsDir)
   250  	if err = newTestConfig(globalMinioDefaultRegion, obj); err != nil {
   251  		t.Fatal(err)
   252  	}
   253  
   254  	creds := globalActiveCred
   255  	policy := "policy"
   256  	testCases := []struct {
   257  		accessKey string
   258  		policy    string
   259  		signature string
   260  		errCode   APIErrorCode
   261  	}{
   262  		{"invalidAccessKey", policy, calculateSignatureV2(policy, creds.SecretKey), ErrInvalidAccessKeyID},
   263  		{creds.AccessKey, policy, calculateSignatureV2("random", creds.SecretKey), ErrSignatureDoesNotMatch},
   264  		{creds.AccessKey, policy, calculateSignatureV2(policy, creds.SecretKey), ErrNone},
   265  	}
   266  	for i, test := range testCases {
   267  		formValues := make(http.Header)
   268  		formValues.Set("Awsaccesskeyid", test.accessKey)
   269  		formValues.Set("Signature", test.signature)
   270  		formValues.Set("Policy", test.policy)
   271  		_, errCode := doesPolicySignatureV2Match(formValues)
   272  		if errCode != test.errCode {
   273  			t.Fatalf("(%d) expected to get %s, instead got %s", i+1, niceError(test.errCode), niceError(errCode))
   274  		}
   275  	}
   276  }