mirror of
https://github.com/restic/restic.git
synced 2026-08-03 12:42:17 +00:00
Update dependencies
Among others, this updates minio-go, so that the new "eu-west-3" zone for AWS is supported.
This commit is contained in:
+16
-16
@@ -17,8 +17,6 @@
|
||||
|
||||
package credentials
|
||||
|
||||
import "fmt"
|
||||
|
||||
// A Chain will search for a provider which returns credentials
|
||||
// and cache that provider until Retrieve is called again.
|
||||
//
|
||||
@@ -27,11 +25,11 @@ import "fmt"
|
||||
// Providers in the list.
|
||||
//
|
||||
// If none of the Providers retrieve valid credentials Value, ChainProvider's
|
||||
// Retrieve() will return the error, collecting all errors from all providers.
|
||||
// Retrieve() will return the no credentials value.
|
||||
//
|
||||
// If a Provider is found which returns valid credentials Value ChainProvider
|
||||
// will cache that Provider for all calls to IsExpired(), until Retrieve is
|
||||
// called again.
|
||||
// called again after IsExpired() is true.
|
||||
//
|
||||
// creds := credentials.NewChainCredentials(
|
||||
// []credentials.Provider{
|
||||
@@ -58,28 +56,30 @@ func NewChainCredentials(providers []Provider) *Credentials {
|
||||
})
|
||||
}
|
||||
|
||||
// Retrieve returns the credentials value or error if no provider returned
|
||||
// without error.
|
||||
// Retrieve returns the credentials value, returns no credentials(anonymous)
|
||||
// if no credentials provider returned any value.
|
||||
//
|
||||
// If a provider is found it will be cached and any calls to IsExpired()
|
||||
// will return the expired state of the cached provider.
|
||||
// If a provider is found with credentials, it will be cached and any calls
|
||||
// to IsExpired() will return the expired state of the cached provider.
|
||||
func (c *Chain) Retrieve() (Value, error) {
|
||||
var errs []error
|
||||
for _, p := range c.Providers {
|
||||
creds, err := p.Retrieve()
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
creds, _ := p.Retrieve()
|
||||
// Always prioritize non-anonymous providers, if any.
|
||||
if creds.AccessKeyID == "" && creds.SecretAccessKey == "" {
|
||||
continue
|
||||
} // Success.
|
||||
}
|
||||
c.curr = p
|
||||
return creds, nil
|
||||
}
|
||||
c.curr = nil
|
||||
return Value{}, fmt.Errorf("No valid providers found %v", errs)
|
||||
// At this point we have exhausted all the providers and
|
||||
// are left without any credentials return anonymous.
|
||||
return Value{
|
||||
SignerType: SignatureAnonymous,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// IsExpired will returned the expired state of the currently cached provider
|
||||
// if there is one. If there is no current provider, true will be returned.
|
||||
// if there is one. If there is no current provider, true will be returned.
|
||||
func (c *Chain) IsExpired() bool {
|
||||
if c.curr != nil {
|
||||
return c.curr.IsExpired()
|
||||
|
||||
+8
-1
@@ -76,7 +76,14 @@ func TestChainGet(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestChainIsExpired(t *testing.T) {
|
||||
credProvider := &credProvider{expired: true}
|
||||
credProvider := &credProvider{
|
||||
creds: Value{
|
||||
AccessKeyID: "UXHW",
|
||||
SecretAccessKey: "MYSECRET",
|
||||
SessionToken: "",
|
||||
},
|
||||
expired: true,
|
||||
}
|
||||
p := &Chain{
|
||||
Providers: []Provider{
|
||||
credProvider,
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/go-ini/ini"
|
||||
homedir "github.com/minio/go-homedir"
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
// A FileAWSCredentials retrieves credentials from the current user's home
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
homedir "github.com/minio/go-homedir"
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
// A FileMinioClient retrieves credentials from the current user's home
|
||||
|
||||
Generated
Vendored
+3
-1
@@ -33,7 +33,7 @@ func TestGetSeedSignature(t *testing.T) {
|
||||
|
||||
req := NewRequest("PUT", "/examplebucket/chunkObject.txt", body)
|
||||
req.Header.Set("x-amz-storage-class", "REDUCED_REDUNDANCY")
|
||||
req.URL.Host = "s3.amazonaws.com"
|
||||
req.Host = "s3.amazonaws.com"
|
||||
|
||||
reqTime, err := time.Parse("20060102T150405Z", "20130524T000000Z")
|
||||
if err != nil {
|
||||
@@ -69,6 +69,7 @@ func TestSetStreamingAuthorization(t *testing.T) {
|
||||
|
||||
req := NewRequest("PUT", "/examplebucket/chunkObject.txt", nil)
|
||||
req.Header.Set("x-amz-storage-class", "REDUCED_REDUNDANCY")
|
||||
req.Host = ""
|
||||
req.URL.Host = "s3.amazonaws.com"
|
||||
|
||||
dataLen := int64(65 * 1024)
|
||||
@@ -93,6 +94,7 @@ func TestStreamingReader(t *testing.T) {
|
||||
req := NewRequest("PUT", "/examplebucket/chunkObject.txt", nil)
|
||||
req.Header.Set("x-amz-storage-class", "REDUCED_REDUNDANCY")
|
||||
req.ContentLength = 65 * 1024
|
||||
req.Host = ""
|
||||
req.URL.Host = "s3.amazonaws.com"
|
||||
|
||||
baseReader := ioutil.NopCloser(bytes.NewReader(bytes.Repeat([]byte("a"), 65*1024)))
|
||||
|
||||
+11
-10
@@ -40,22 +40,23 @@ const (
|
||||
)
|
||||
|
||||
// Encode input URL path to URL encoded path.
|
||||
func encodeURL2Path(u *url.URL) (path string) {
|
||||
func encodeURL2Path(req *http.Request) (path string) {
|
||||
reqHost := getHostAddr(req)
|
||||
// Encode URL path.
|
||||
if isS3, _ := filepath.Match("*.s3*.amazonaws.com", u.Host); isS3 {
|
||||
bucketName := u.Host[:strings.LastIndex(u.Host, ".s3")]
|
||||
if isS3, _ := filepath.Match("*.s3*.amazonaws.com", reqHost); isS3 {
|
||||
bucketName := reqHost[:strings.LastIndex(reqHost, ".s3")]
|
||||
path = "/" + bucketName
|
||||
path += u.Path
|
||||
path += req.URL.Path
|
||||
path = s3utils.EncodePath(path)
|
||||
return
|
||||
}
|
||||
if strings.HasSuffix(u.Host, ".storage.googleapis.com") {
|
||||
path = "/" + strings.TrimSuffix(u.Host, ".storage.googleapis.com")
|
||||
path += u.Path
|
||||
if strings.HasSuffix(reqHost, ".storage.googleapis.com") {
|
||||
path = "/" + strings.TrimSuffix(reqHost, ".storage.googleapis.com")
|
||||
path += req.URL.Path
|
||||
path = s3utils.EncodePath(path)
|
||||
return
|
||||
}
|
||||
path = s3utils.EncodePath(u.Path)
|
||||
path = s3utils.EncodePath(req.URL.Path)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -86,7 +87,7 @@ func PreSignV2(req http.Request, accessKeyID, secretAccessKey string, expires in
|
||||
|
||||
query := req.URL.Query()
|
||||
// Handle specially for Google Cloud Storage.
|
||||
if strings.Contains(req.URL.Host, ".storage.googleapis.com") {
|
||||
if strings.Contains(getHostAddr(&req), ".storage.googleapis.com") {
|
||||
query.Set("GoogleAccessId", accessKeyID)
|
||||
} else {
|
||||
query.Set("AWSAccessKeyId", accessKeyID)
|
||||
@@ -291,7 +292,7 @@ func writeCanonicalizedResource(buf *bytes.Buffer, req http.Request) {
|
||||
// Save request URL.
|
||||
requestURL := req.URL
|
||||
// Get encoded URL path.
|
||||
buf.WriteString(encodeURL2Path(requestURL))
|
||||
buf.WriteString(encodeURL2Path(&req))
|
||||
if requestURL.RawQuery != "" {
|
||||
var n int
|
||||
vals, _ := url.ParseQuery(requestURL.RawQuery)
|
||||
|
||||
+1
-1
@@ -144,7 +144,7 @@ func getCanonicalHeaders(req http.Request, ignoredHeaders map[string]bool) strin
|
||||
buf.WriteByte(':')
|
||||
switch {
|
||||
case k == "host":
|
||||
buf.WriteString(req.URL.Host)
|
||||
buf.WriteString(getHostAddr(&req))
|
||||
fallthrough
|
||||
default:
|
||||
for idx, v := range vals[k] {
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Minio Go Library for Amazon S3 Compatible Cloud Storage
|
||||
* Copyright 2015-2017 Minio, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package s3signer
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRequestHost(t *testing.T) {
|
||||
req, _ := buildRequest("dynamodb", "us-east-1", "{}")
|
||||
req.URL.RawQuery = "Foo=z&Foo=o&Foo=m&Foo=a"
|
||||
req.Host = "myhost"
|
||||
canonicalHeaders := getCanonicalHeaders(*req, v4IgnoredHeaders)
|
||||
|
||||
if !strings.Contains(canonicalHeaders, "host:"+req.Host) {
|
||||
t.Errorf("canonical host header invalid")
|
||||
}
|
||||
}
|
||||
|
||||
func buildRequest(serviceName, region, body string) (*http.Request, io.ReadSeeker) {
|
||||
endpoint := "https://" + serviceName + "." + region + ".amazonaws.com"
|
||||
reader := strings.NewReader(body)
|
||||
req, _ := http.NewRequest("POST", endpoint, reader)
|
||||
req.URL.Opaque = "//example.org/bucket/key-._~,!@#$%^&*()"
|
||||
req.Header.Add("X-Amz-Target", "prefix.Operation")
|
||||
req.Header.Add("Content-Type", "application/x-amz-json-1.0")
|
||||
req.Header.Add("Content-Length", string(len(body)))
|
||||
req.Header.Add("X-Amz-Meta-Other-Header", "some-value=!@#$%^&* (+)")
|
||||
req.Header.Add("X-Amz-Meta-Other-Header_With_Underscore", "some-value=!@#$%^&* (+)")
|
||||
req.Header.Add("X-amz-Meta-Other-Header_With_Underscore", "some-value=!@#$%^&* (+)")
|
||||
return req, reader
|
||||
}
|
||||
+9
@@ -20,6 +20,7 @@ package s3signer
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// unsignedPayload - value to be set to X-Amz-Content-Sha256 header when
|
||||
@@ -38,3 +39,11 @@ func sumHMAC(key []byte, data []byte) []byte {
|
||||
hash.Write(data)
|
||||
return hash.Sum(nil)
|
||||
}
|
||||
|
||||
// getHostAddr returns host header if available, otherwise returns host from URL
|
||||
func getHostAddr(req *http.Request) string {
|
||||
if req.Host != "" {
|
||||
return req.Host
|
||||
}
|
||||
return req.URL.Host
|
||||
}
|
||||
|
||||
+2
-1
@@ -19,6 +19,7 @@ package s3signer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
)
|
||||
@@ -66,7 +67,7 @@ func TestEncodeURL2Path(t *testing.T) {
|
||||
t.Fatal("Error:", err)
|
||||
}
|
||||
urlPath := "/" + bucketName + "/" + o.encodedObjName
|
||||
if urlPath != encodeURL2Path(u) {
|
||||
if urlPath != encodeURL2Path(&http.Request{URL: u}) {
|
||||
t.Fatal("Error")
|
||||
}
|
||||
}
|
||||
|
||||
+45
-20
@@ -81,18 +81,56 @@ func IsVirtualHostSupported(endpointURL url.URL, bucketName string) bool {
|
||||
return IsAmazonEndpoint(endpointURL) || IsGoogleEndpoint(endpointURL)
|
||||
}
|
||||
|
||||
// AmazonS3Host - regular expression used to determine if an arg is s3 host.
|
||||
var AmazonS3Host = regexp.MustCompile("^s3[.-]?(.*?)\\.amazonaws\\.com$")
|
||||
// Refer for region styles - https://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region
|
||||
|
||||
// amazonS3HostHyphen - regular expression used to determine if an arg is s3 host in hyphenated style.
|
||||
var amazonS3HostHyphen = regexp.MustCompile(`^s3-(.*?)\.amazonaws\.com$`)
|
||||
|
||||
// amazonS3HostDualStack - regular expression used to determine if an arg is s3 host dualstack.
|
||||
var amazonS3HostDualStack = regexp.MustCompile(`^s3\.dualstack\.(.*?)\.amazonaws\.com$`)
|
||||
|
||||
// amazonS3HostDot - regular expression used to determine if an arg is s3 host in . style.
|
||||
var amazonS3HostDot = regexp.MustCompile(`^s3\.(.*?)\.amazonaws\.com$`)
|
||||
|
||||
// amazonS3ChinaHost - regular expression used to determine if the arg is s3 china host.
|
||||
var amazonS3ChinaHost = regexp.MustCompile(`^s3\.(cn.*?)\.amazonaws\.com\.cn$`)
|
||||
|
||||
// GetRegionFromURL - returns a region from url host.
|
||||
func GetRegionFromURL(endpointURL url.URL) string {
|
||||
if endpointURL == sentinelURL {
|
||||
return ""
|
||||
}
|
||||
if endpointURL.Host == "s3-external-1.amazonaws.com" {
|
||||
return ""
|
||||
}
|
||||
if IsAmazonGovCloudEndpoint(endpointURL) {
|
||||
return "us-gov-west-1"
|
||||
}
|
||||
parts := amazonS3HostDualStack.FindStringSubmatch(endpointURL.Host)
|
||||
if len(parts) > 1 {
|
||||
return parts[1]
|
||||
}
|
||||
parts = amazonS3HostHyphen.FindStringSubmatch(endpointURL.Host)
|
||||
if len(parts) > 1 {
|
||||
return parts[1]
|
||||
}
|
||||
parts = amazonS3ChinaHost.FindStringSubmatch(endpointURL.Host)
|
||||
if len(parts) > 1 {
|
||||
return parts[1]
|
||||
}
|
||||
parts = amazonS3HostDot.FindStringSubmatch(endpointURL.Host)
|
||||
if len(parts) > 1 {
|
||||
return parts[1]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// IsAmazonEndpoint - Match if it is exactly Amazon S3 endpoint.
|
||||
func IsAmazonEndpoint(endpointURL url.URL) bool {
|
||||
if IsAmazonChinaEndpoint(endpointURL) {
|
||||
if endpointURL.Host == "s3-external-1.amazonaws.com" || endpointURL.Host == "s3.amazonaws.com" {
|
||||
return true
|
||||
}
|
||||
if IsAmazonGovCloudEndpoint(endpointURL) {
|
||||
return true
|
||||
}
|
||||
return AmazonS3Host.MatchString(endpointURL.Host)
|
||||
return GetRegionFromURL(endpointURL) != ""
|
||||
}
|
||||
|
||||
// IsAmazonGovCloudEndpoint - Match if it is exactly Amazon S3 GovCloud endpoint.
|
||||
@@ -112,19 +150,6 @@ func IsAmazonFIPSGovCloudEndpoint(endpointURL url.URL) bool {
|
||||
return endpointURL.Host == "s3-fips-us-gov-west-1.amazonaws.com"
|
||||
}
|
||||
|
||||
// IsAmazonChinaEndpoint - Match if it is exactly Amazon S3 China endpoint.
|
||||
// Customers who wish to use the new Beijing Region are required
|
||||
// to sign up for a separate set of account credentials unique to
|
||||
// the China (Beijing) Region. Customers with existing AWS credentials
|
||||
// will not be able to access resources in the new Region, and vice versa.
|
||||
// For more info https://aws.amazon.com/about-aws/whats-new/2013/12/18/announcing-the-aws-china-beijing-region/
|
||||
func IsAmazonChinaEndpoint(endpointURL url.URL) bool {
|
||||
if endpointURL == sentinelURL {
|
||||
return false
|
||||
}
|
||||
return endpointURL.Host == "s3.cn-north-1.amazonaws.com.cn"
|
||||
}
|
||||
|
||||
// IsGoogleEndpoint - Match if it is exactly Google cloud storage endpoint.
|
||||
func IsGoogleEndpoint(endpointURL url.URL) bool {
|
||||
if endpointURL == sentinelURL {
|
||||
|
||||
+69
-35
@@ -23,6 +23,66 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Tests get region from host URL.
|
||||
func TestGetRegionFromURL(t *testing.T) {
|
||||
testCases := []struct {
|
||||
u url.URL
|
||||
expectedRegion string
|
||||
}{
|
||||
{
|
||||
u: url.URL{Host: "storage.googleapis.com"},
|
||||
expectedRegion: "",
|
||||
},
|
||||
{
|
||||
u: url.URL{Host: "s3.cn-north-1.amazonaws.com.cn"},
|
||||
expectedRegion: "cn-north-1",
|
||||
},
|
||||
{
|
||||
u: url.URL{Host: "s3.cn-northwest-1.amazonaws.com.cn"},
|
||||
expectedRegion: "cn-northwest-1",
|
||||
},
|
||||
{
|
||||
u: url.URL{Host: "s3-fips-us-gov-west-1.amazonaws.com"},
|
||||
expectedRegion: "us-gov-west-1",
|
||||
},
|
||||
{
|
||||
u: url.URL{Host: "s3-us-gov-west-1.amazonaws.com"},
|
||||
expectedRegion: "us-gov-west-1",
|
||||
},
|
||||
{
|
||||
u: url.URL{Host: "192.168.1.1"},
|
||||
expectedRegion: "",
|
||||
},
|
||||
{
|
||||
u: url.URL{Host: "s3-eu-west-1.amazonaws.com"},
|
||||
expectedRegion: "eu-west-1",
|
||||
},
|
||||
{
|
||||
u: url.URL{Host: "s3.eu-west-1.amazonaws.com"},
|
||||
expectedRegion: "eu-west-1",
|
||||
},
|
||||
{
|
||||
u: url.URL{Host: "s3.dualstack.eu-west-1.amazonaws.com"},
|
||||
expectedRegion: "eu-west-1",
|
||||
},
|
||||
{
|
||||
u: url.URL{Host: "s3.amazonaws.com"},
|
||||
expectedRegion: "",
|
||||
},
|
||||
{
|
||||
u: url.URL{Host: "s3-external-1.amazonaws.com"},
|
||||
expectedRegion: "",
|
||||
},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
region := GetRegionFromURL(testCase.u)
|
||||
if testCase.expectedRegion != region {
|
||||
t.Errorf("Test %d: Expected region %s, got %s", i+1, testCase.expectedRegion, region)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests for 'isValidDomain(host string) bool'.
|
||||
func TestIsValidDomain(t *testing.T) {
|
||||
testCases := []struct {
|
||||
@@ -33,6 +93,7 @@ func TestIsValidDomain(t *testing.T) {
|
||||
}{
|
||||
{"s3.amazonaws.com", true},
|
||||
{"s3.cn-north-1.amazonaws.com.cn", true},
|
||||
{"s3.cn-northwest-1.amazonaws.com.cn", true},
|
||||
{"s3.amazonaws.com_", false},
|
||||
{"%$$$", false},
|
||||
{"s3.amz.test.com", true},
|
||||
@@ -120,9 +181,17 @@ func TestIsAmazonEndpoint(t *testing.T) {
|
||||
{"https://amazons3.amazonaws.com", false},
|
||||
{"-192.168.1.1", false},
|
||||
{"260.192.1.1", false},
|
||||
{"https://s3-.amazonaws.com", false},
|
||||
{"https://s3..amazonaws.com", false},
|
||||
{"https://s3.dualstack.us-west-1.amazonaws.com.cn", false},
|
||||
{"https://s3..us-west-1.amazonaws.com.cn", false},
|
||||
// valid inputs.
|
||||
{"https://s3.amazonaws.com", true},
|
||||
{"https://s3-external-1.amazonaws.com", true},
|
||||
{"https://s3.cn-north-1.amazonaws.com.cn", true},
|
||||
{"https://s3-us-west-1.amazonaws.com", true},
|
||||
{"https://s3.us-west-1.amazonaws.com", true},
|
||||
{"https://s3.dualstack.us-west-1.amazonaws.com", true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
@@ -138,41 +207,6 @@ func TestIsAmazonEndpoint(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
// Tests validate Amazon S3 China endpoint validator.
|
||||
func TestIsAmazonChinaEndpoint(t *testing.T) {
|
||||
testCases := []struct {
|
||||
url string
|
||||
// Expected result.
|
||||
result bool
|
||||
}{
|
||||
{"https://192.168.1.1", false},
|
||||
{"192.168.1.1", false},
|
||||
{"http://storage.googleapis.com", false},
|
||||
{"https://storage.googleapis.com", false},
|
||||
{"storage.googleapis.com", false},
|
||||
{"s3.amazonaws.com", false},
|
||||
{"https://amazons3.amazonaws.com", false},
|
||||
{"-192.168.1.1", false},
|
||||
{"260.192.1.1", false},
|
||||
// s3.amazonaws.com is not a valid Amazon S3 China end point.
|
||||
{"https://s3.amazonaws.com", false},
|
||||
// valid input.
|
||||
{"https://s3.cn-north-1.amazonaws.com.cn", true},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
u, err := url.Parse(testCase.url)
|
||||
if err != nil {
|
||||
t.Errorf("Test %d: Expected to pass, but failed with: <ERROR> %s", i+1, err)
|
||||
}
|
||||
result := IsAmazonChinaEndpoint(*u)
|
||||
if testCase.result != result {
|
||||
t.Errorf("Test %d: Expected isAmazonEndpoint to be '%v' for input \"%s\", but found it to be '%v' instead", i+1, testCase.result, testCase.url, result)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Tests validate Google Cloud end point validator.
|
||||
func TestIsGoogleEndpoint(t *testing.T) {
|
||||
testCases := []struct {
|
||||
|
||||
Reference in New Issue
Block a user