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 }