storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/signature-v2_test.go (about)

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