github.com/minio/minio-go/v6@v6.0.57/utils_test.go (about)

     1  /*
     2   * MinIO Go Library for Amazon S3 Compatible Cloud Storage
     3   * Copyright 2015-2017 MinIO, Inc.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package minio
    19  
    20  import (
    21  	"fmt"
    22  	"net/url"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/minio/minio-go/v6/pkg/s3utils"
    27  )
    28  
    29  // Tests signature redacting function used
    30  // in filtering on-wire Authorization header.
    31  func TestRedactSignature(t *testing.T) {
    32  	testCases := []struct {
    33  		authValue                 string
    34  		expectedRedactedAuthValue string
    35  	}{
    36  		{
    37  			authValue:                 "AWS 1231313:888x000231==",
    38  			expectedRedactedAuthValue: "AWS **REDACTED**:**REDACTED**",
    39  		},
    40  		{
    41  			authValue:                 "AWS4-HMAC-SHA256 Credential=12312313/20170613/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=02131231312313213",
    42  			expectedRedactedAuthValue: "AWS4-HMAC-SHA256 Credential=**REDACTED**/20170613/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=**REDACTED**",
    43  		},
    44  	}
    45  
    46  	for i, testCase := range testCases {
    47  		redactedAuthValue := redactSignature(testCase.authValue)
    48  		if redactedAuthValue != testCase.expectedRedactedAuthValue {
    49  			t.Errorf("Test %d: Expected %s, got %s", i+1, testCase.expectedRedactedAuthValue, redactedAuthValue)
    50  		}
    51  	}
    52  }
    53  
    54  // Tests for 'getEndpointURL(endpoint string, inSecure bool)'.
    55  func TestGetEndpointURL(t *testing.T) {
    56  	testCases := []struct {
    57  		// Inputs.
    58  		endPoint string
    59  		secure   bool
    60  
    61  		// Expected result.
    62  		result string
    63  		err    error
    64  		// Flag indicating whether the test is expected to pass or not.
    65  		shouldPass bool
    66  	}{
    67  		{"s3.amazonaws.com", true, "https://s3.amazonaws.com", nil, true},
    68  		{"s3.cn-north-1.amazonaws.com.cn", true, "https://s3.cn-north-1.amazonaws.com.cn", nil, true},
    69  		{"s3.cn-northwest-1.amazonaws.com.cn", true, "https://s3.cn-northwest-1.amazonaws.com.cn", nil, true},
    70  		{"s3.amazonaws.com", false, "http://s3.amazonaws.com", nil, true},
    71  		{"s3.cn-north-1.amazonaws.com.cn", false, "http://s3.cn-north-1.amazonaws.com.cn", nil, true},
    72  		{"s3.cn-northwest-1.amazonaws.com.cn", false, "http://s3.cn-northwest-1.amazonaws.com.cn", nil, true},
    73  		{"192.168.1.1:9000", false, "http://192.168.1.1:9000", nil, true},
    74  		{"192.168.1.1:9000", true, "https://192.168.1.1:9000", nil, true},
    75  		{"s3.amazonaws.com:443", true, "https://s3.amazonaws.com:443", nil, true},
    76  		{"13333.123123.-", true, "", ErrInvalidArgument(fmt.Sprintf("Endpoint: %s does not follow ip address or domain name standards.", "13333.123123.-")), false},
    77  		{"13333.123123.-", true, "", ErrInvalidArgument(fmt.Sprintf("Endpoint: %s does not follow ip address or domain name standards.", "13333.123123.-")), false},
    78  		{"storage.googleapis.com:4000", true, "", ErrInvalidArgument("Google Cloud Storage endpoint should be 'storage.googleapis.com'."), false},
    79  		{"s3.aamzza.-", true, "", ErrInvalidArgument(fmt.Sprintf("Endpoint: %s does not follow ip address or domain name standards.", "s3.aamzza.-")), false},
    80  		{"", true, "", ErrInvalidArgument("Endpoint:  does not follow ip address or domain name standards."), false},
    81  	}
    82  
    83  	for i, testCase := range testCases {
    84  		result, err := getEndpointURL(testCase.endPoint, testCase.secure)
    85  		if err != nil && testCase.shouldPass {
    86  			t.Errorf("Test %d: Expected to pass, but failed with: <ERROR> %s", i+1, err.Error())
    87  		}
    88  		if err == nil && !testCase.shouldPass {
    89  			t.Errorf("Test %d: Expected to fail with <ERROR> \"%s\", but passed instead", i+1, testCase.err.Error())
    90  		}
    91  		// Failed as expected, but does it fail for the expected reason.
    92  		if err != nil && !testCase.shouldPass {
    93  			if err.Error() != testCase.err.Error() {
    94  				t.Errorf("Test %d: Expected to fail with error \"%s\", but instead failed with error \"%s\" instead", i+1, testCase.err.Error(), err.Error())
    95  			}
    96  		}
    97  
    98  		// Test passes as expected, but the output values are verified for correctness here.
    99  		if err == nil && testCase.shouldPass {
   100  			if testCase.result != result.String() {
   101  				t.Errorf("Test %d: Expected the result Url to be \"%s\", but found \"%s\" instead", i+1, testCase.result, result.String())
   102  			}
   103  		}
   104  	}
   105  }
   106  
   107  // Tests validate end point validator.
   108  func TestIsValidEndpointURL(t *testing.T) {
   109  	testCases := []struct {
   110  		url string
   111  		err error
   112  		// Flag indicating whether the test is expected to pass or not.
   113  		shouldPass bool
   114  	}{
   115  		{"", ErrInvalidArgument("Endpoint url cannot be empty."), false},
   116  		{"/", nil, true},
   117  		{"https://s3.amazonaws.com", nil, true},
   118  		{"https://s3.cn-north-1.amazonaws.com.cn", nil, true},
   119  		{"https://s3-us-gov-west-1.amazonaws.com", nil, true},
   120  		{"https://s3-fips-us-gov-west-1.amazonaws.com", nil, true},
   121  		{"https://s3.amazonaws.com/", nil, true},
   122  		{"https://storage.googleapis.com/", nil, true},
   123  		{"https://z3.amazonaws.com", nil, true},
   124  		{"https://mybalancer.us-east-1.elb.amazonaws.com", nil, true},
   125  		{"192.168.1.1", ErrInvalidArgument("Endpoint url cannot have fully qualified paths."), false},
   126  		{"https://amazon.googleapis.com/", ErrInvalidArgument("Google Cloud Storage endpoint should be 'storage.googleapis.com'."), false},
   127  		{"https://storage.googleapis.com/bucket/", ErrInvalidArgument("Endpoint url cannot have fully qualified paths."), false},
   128  		{"https://s3.amazonaws.com/bucket/object", ErrInvalidArgument("Endpoint url cannot have fully qualified paths."), false},
   129  	}
   130  
   131  	for i, testCase := range testCases {
   132  		var u url.URL
   133  		if testCase.url == "" {
   134  			u = sentinelURL
   135  		} else {
   136  			u1, err := url.Parse(testCase.url)
   137  			if err != nil {
   138  				t.Errorf("Test %d: Expected to pass, but failed with: <ERROR> %s", i+1, err)
   139  			}
   140  			u = *u1
   141  		}
   142  		err := isValidEndpointURL(u)
   143  		if err != nil && testCase.shouldPass {
   144  			t.Errorf("Test %d: Expected to pass, but failed with: <ERROR> %s", i+1, err)
   145  		}
   146  		if err == nil && !testCase.shouldPass {
   147  			t.Errorf("Test %d: Expected to fail with <ERROR> \"%s\", but passed instead", i+1, testCase.err)
   148  		}
   149  		// Failed as expected, but does it fail for the expected reason.
   150  		if err != nil && !testCase.shouldPass {
   151  			if err.Error() != testCase.err.Error() {
   152  				t.Errorf("Test %d: Expected to fail with error \"%s\", but instead failed with error \"%s\" instead", i+1, testCase.err, err)
   153  			}
   154  		}
   155  
   156  	}
   157  }
   158  
   159  func TestDefaultBucketLocation(t *testing.T) {
   160  	testCases := []struct {
   161  		endpointURL      url.URL
   162  		regionOverride   string
   163  		expectedLocation string
   164  	}{
   165  		// Region override is set URL is ignored. - Test 1.
   166  		{
   167  			endpointURL:      url.URL{Host: "s3-fips-us-gov-west-1.amazonaws.com"},
   168  			regionOverride:   "us-west-1",
   169  			expectedLocation: "us-west-1",
   170  		},
   171  		// No region override, url based preferenced is honored - Test 2.
   172  		{
   173  			endpointURL:      url.URL{Host: "s3-fips-us-gov-west-1.amazonaws.com"},
   174  			regionOverride:   "",
   175  			expectedLocation: "us-gov-west-1",
   176  		},
   177  		// Region override is honored - Test 3.
   178  		{
   179  			endpointURL:      url.URL{Host: "s3.amazonaws.com"},
   180  			regionOverride:   "us-west-1",
   181  			expectedLocation: "us-west-1",
   182  		},
   183  		// China region should be honored, region override not provided. - Test 4.
   184  		{
   185  			endpointURL:      url.URL{Host: "s3.cn-north-1.amazonaws.com.cn"},
   186  			regionOverride:   "",
   187  			expectedLocation: "cn-north-1",
   188  		},
   189  		// China region should be honored, region override not provided. - Test 5.
   190  		{
   191  			endpointURL:      url.URL{Host: "s3.cn-northwest-1.amazonaws.com.cn"},
   192  			regionOverride:   "",
   193  			expectedLocation: "cn-northwest-1",
   194  		},
   195  		// No region provided, no standard region strings provided as well. - Test 6.
   196  		{
   197  			endpointURL:      url.URL{Host: "s3.amazonaws.com"},
   198  			regionOverride:   "",
   199  			expectedLocation: "us-east-1",
   200  		},
   201  	}
   202  
   203  	for i, testCase := range testCases {
   204  		retLocation := getDefaultLocation(testCase.endpointURL, testCase.regionOverride)
   205  		if testCase.expectedLocation != retLocation {
   206  			t.Errorf("Test %d: Expected location %s, got %s", i+1, testCase.expectedLocation, retLocation)
   207  		}
   208  	}
   209  }
   210  
   211  // Tests validate the expiry time validator.
   212  func TestIsValidExpiry(t *testing.T) {
   213  	testCases := []struct {
   214  		// Input.
   215  		duration time.Duration
   216  		// Expected result.
   217  		err error
   218  		// Flag to indicate whether the test should pass.
   219  		shouldPass bool
   220  	}{
   221  		{100 * time.Millisecond, ErrInvalidArgument("Expires cannot be lesser than 1 second."), false},
   222  		{604801 * time.Second, ErrInvalidArgument("Expires cannot be greater than 7 days."), false},
   223  		{0 * time.Second, ErrInvalidArgument("Expires cannot be lesser than 1 second."), false},
   224  		{1 * time.Second, nil, true},
   225  		{10000 * time.Second, nil, true},
   226  		{999 * time.Second, nil, true},
   227  	}
   228  
   229  	for i, testCase := range testCases {
   230  		err := isValidExpiry(testCase.duration)
   231  		if err != nil && testCase.shouldPass {
   232  			t.Errorf("Test %d: Expected to pass, but failed with: <ERROR> %s", i+1, err.Error())
   233  		}
   234  		if err == nil && !testCase.shouldPass {
   235  			t.Errorf("Test %d: Expected to fail with <ERROR> \"%s\", but passed instead", i+1, testCase.err.Error())
   236  		}
   237  		// Failed as expected, but does it fail for the expected reason.
   238  		if err != nil && !testCase.shouldPass {
   239  			if err.Error() != testCase.err.Error() {
   240  				t.Errorf("Test %d: Expected to fail with error \"%s\", but instead failed with error \"%s\" instead", i+1, testCase.err.Error(), err.Error())
   241  			}
   242  		}
   243  
   244  	}
   245  }
   246  
   247  // Tests validate the bucket name validator.
   248  func TestIsValidBucketName(t *testing.T) {
   249  	testCases := []struct {
   250  		// Input.
   251  		bucketName string
   252  		// Expected result.
   253  		err error
   254  		// Flag to indicate whether test should Pass.
   255  		shouldPass bool
   256  	}{
   257  		{".mybucket", ErrInvalidBucketName("Bucket name contains invalid characters"), false},
   258  		{"mybucket.", ErrInvalidBucketName("Bucket name contains invalid characters"), false},
   259  		{"mybucket-", ErrInvalidBucketName("Bucket name contains invalid characters"), false},
   260  		{"my", ErrInvalidBucketName("Bucket name cannot be shorter than 3 characters"), false},
   261  		{"", ErrInvalidBucketName("Bucket name cannot be empty"), false},
   262  		{"my..bucket", ErrInvalidBucketName("Bucket name contains invalid characters"), false},
   263  		{"my.bucket.com", nil, true},
   264  		{"my-bucket", nil, true},
   265  		{"123my-bucket", nil, true},
   266  	}
   267  
   268  	for i, testCase := range testCases {
   269  		err := s3utils.CheckValidBucketName(testCase.bucketName)
   270  		if err != nil && testCase.shouldPass {
   271  			t.Errorf("Test %d: Expected to pass, but failed with: <ERROR> %s", i+1, err.Error())
   272  		}
   273  		if err == nil && !testCase.shouldPass {
   274  			t.Errorf("Test %d: Expected to fail with <ERROR> \"%s\", but passed instead", i+1, testCase.err.Error())
   275  		}
   276  		// Failed as expected, but does it fail for the expected reason.
   277  		if err != nil && !testCase.shouldPass {
   278  			if err.Error() != testCase.err.Error() {
   279  				t.Errorf("Test %d: Expected to fail with error \"%s\", but instead failed with error \"%s\" instead", i+1, testCase.err.Error(), err.Error())
   280  			}
   281  		}
   282  
   283  	}
   284  
   285  }
   286  
   287  // Tests if header is standard supported header
   288  func TestIsStandardHeader(t *testing.T) {
   289  	testCases := []struct {
   290  		// Input.
   291  		header string
   292  		// Expected result.
   293  		expectedValue bool
   294  	}{
   295  		{"content-encoding", true},
   296  		{"content-type", true},
   297  		{"cache-control", true},
   298  		{"content-disposition", true},
   299  		{"content-language", true},
   300  		{"random-header", false},
   301  	}
   302  
   303  	for i, testCase := range testCases {
   304  		actual := isStandardHeader(testCase.header)
   305  		if actual != testCase.expectedValue {
   306  			t.Errorf("Test %d: Expected to pass, but failed", i+1)
   307  		}
   308  	}
   309  
   310  }
   311  
   312  // Tests if header is server encryption header
   313  func TestIsSSEHeader(t *testing.T) {
   314  	testCases := []struct {
   315  		// Input.
   316  		header string
   317  		// Expected result.
   318  		expectedValue bool
   319  	}{
   320  		{"x-amz-server-side-encryption", true},
   321  		{"x-amz-server-side-encryption-aws-kms-key-id", true},
   322  		{"x-amz-server-side-encryption-context", true},
   323  		{"x-amz-server-side-encryption-customer-algorithm", true},
   324  		{"x-amz-server-side-encryption-customer-key", true},
   325  		{"x-amz-server-side-encryption-customer-key-MD5", true},
   326  		{"random-header", false},
   327  	}
   328  
   329  	for i, testCase := range testCases {
   330  		actual := isSSEHeader(testCase.header)
   331  		if actual != testCase.expectedValue {
   332  			t.Errorf("Test %d: Expected to pass, but failed", i+1)
   333  		}
   334  	}
   335  }
   336  
   337  // Tests if header is x-amz-meta or x-amz-acl
   338  func TestIsAmzHeader(t *testing.T) {
   339  	testCases := []struct {
   340  		// Input.
   341  		header string
   342  		// Expected result.
   343  		expectedValue bool
   344  	}{
   345  		{"x-amz-iv", false},
   346  		{"x-amz-key", false},
   347  		{"x-amz-matdesc", false},
   348  		{"x-amz-meta-x-amz-iv", true},
   349  		{"x-amz-meta-x-amz-key", true},
   350  		{"x-amz-meta-x-amz-matdesc", true},
   351  		{"x-amz-acl", true},
   352  		{"random-header", false},
   353  	}
   354  
   355  	for i, testCase := range testCases {
   356  		actual := isAmzHeader(testCase.header)
   357  		if actual != testCase.expectedValue {
   358  			t.Errorf("Test %d: Expected to pass, but failed", i+1)
   359  		}
   360  	}
   361  
   362  }