summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/minio/minio-go/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/minio/minio-go/pkg')
-rw-r--r--vendor/github.com/minio/minio-go/pkg/credentials/chain.go89
-rw-r--r--vendor/github.com/minio/minio-go/pkg/credentials/chain_test.go137
-rw-r--r--vendor/github.com/minio/minio-go/pkg/credentials/config.json.sample17
-rw-r--r--vendor/github.com/minio/minio-go/pkg/credentials/credentials.go175
-rw-r--r--vendor/github.com/minio/minio-go/pkg/credentials/credentials.sample12
-rw-r--r--vendor/github.com/minio/minio-go/pkg/credentials/credentials_test.go73
-rw-r--r--vendor/github.com/minio/minio-go/pkg/credentials/doc.go45
-rw-r--r--vendor/github.com/minio/minio-go/pkg/credentials/env_aws.go71
-rw-r--r--vendor/github.com/minio/minio-go/pkg/credentials/env_minio.go62
-rw-r--r--vendor/github.com/minio/minio-go/pkg/credentials/env_test.go105
-rw-r--r--vendor/github.com/minio/minio-go/pkg/credentials/file_aws_credentials.go120
-rw-r--r--vendor/github.com/minio/minio-go/pkg/credentials/file_minio_client.go129
-rw-r--r--vendor/github.com/minio/minio-go/pkg/credentials/file_test.go189
-rw-r--r--vendor/github.com/minio/minio-go/pkg/credentials/iam_aws.go196
-rw-r--r--vendor/github.com/minio/minio-go/pkg/credentials/iam_aws_test.go180
-rw-r--r--vendor/github.com/minio/minio-go/pkg/credentials/signature-type.go76
-rw-r--r--vendor/github.com/minio/minio-go/pkg/credentials/static.go67
-rw-r--r--vendor/github.com/minio/minio-go/pkg/credentials/static_test.go68
-rw-r--r--vendor/github.com/minio/minio-go/pkg/encrypt/cbc.go284
-rw-r--r--vendor/github.com/minio/minio-go/pkg/encrypt/interface.go50
-rw-r--r--vendor/github.com/minio/minio-go/pkg/encrypt/keys.go165
-rw-r--r--vendor/github.com/minio/minio-go/pkg/policy/bucket-policy.go2
-rw-r--r--vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-streaming.go290
-rw-r--r--vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-streaming_test.go106
-rw-r--r--vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-v2.go2
-rw-r--r--vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-v4.go33
-rw-r--r--vendor/github.com/minio/minio-go/pkg/s3signer/request-signature_test.go8
-rw-r--r--vendor/github.com/minio/minio-go/pkg/s3signer/test-utils_test.go103
28 files changed, 2836 insertions, 18 deletions
diff --git a/vendor/github.com/minio/minio-go/pkg/credentials/chain.go b/vendor/github.com/minio/minio-go/pkg/credentials/chain.go
new file mode 100644
index 000000000..6b0e57440
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/pkg/credentials/chain.go
@@ -0,0 +1,89 @@
+/*
+ * Minio Go Library for Amazon S3 Compatible Cloud Storage
+ * (C) 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 credentials
+
+import "fmt"
+
+// A Chain will search for a provider which returns credentials
+// and cache that provider until Retrieve is called again.
+//
+// The Chain provides a way of chaining multiple providers together
+// which will pick the first available using priority order of the
+// 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.
+//
+// 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.
+//
+// creds := credentials.NewChainCredentials(
+// []credentials.Provider{
+// &credentials.EnvAWSS3{},
+// &credentials.EnvMinio{},
+// })
+//
+// // Usage of ChainCredentials.
+// mc, err := minio.NewWithCredentials(endpoint, creds, secure, "us-east-1")
+// if err != nil {
+// log.Fatalln(err)
+// }
+//
+type Chain struct {
+ Providers []Provider
+ curr Provider
+}
+
+// NewChainCredentials returns a pointer to a new Credentials object
+// wrapping a chain of providers.
+func NewChainCredentials(providers []Provider) *Credentials {
+ return New(&Chain{
+ Providers: append([]Provider{}, providers...),
+ })
+}
+
+// Retrieve returns the credentials value or error if no provider returned
+// without error.
+//
+// If a provider is found 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)
+ continue
+ } // Success.
+ c.curr = p
+ return creds, nil
+ }
+ c.curr = nil
+ return Value{}, fmt.Errorf("No valid providers found %v", errs)
+}
+
+// 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.
+func (c *Chain) IsExpired() bool {
+ if c.curr != nil {
+ return c.curr.IsExpired()
+ }
+
+ return true
+}
diff --git a/vendor/github.com/minio/minio-go/pkg/credentials/chain_test.go b/vendor/github.com/minio/minio-go/pkg/credentials/chain_test.go
new file mode 100644
index 000000000..cb5a6dda5
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/pkg/credentials/chain_test.go
@@ -0,0 +1,137 @@
+/*
+ * Minio Go Library for Amazon S3 Compatible Cloud Storage
+ * (C) 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 credentials
+
+import (
+ "errors"
+ "testing"
+)
+
+type testCredProvider struct {
+ creds Value
+ expired bool
+ err error
+}
+
+func (s *testCredProvider) Retrieve() (Value, error) {
+ s.expired = false
+ return s.creds, s.err
+}
+func (s *testCredProvider) IsExpired() bool {
+ return s.expired
+}
+
+func TestChainGet(t *testing.T) {
+ p := &Chain{
+ Providers: []Provider{
+ &credProvider{err: errors.New("FirstError")},
+ &credProvider{err: errors.New("SecondError")},
+ &testCredProvider{
+ creds: Value{
+ AccessKeyID: "AKIF",
+ SecretAccessKey: "NOSECRET",
+ SessionToken: "",
+ },
+ },
+ &credProvider{
+ creds: Value{
+ AccessKeyID: "AKID",
+ SecretAccessKey: "SECRET",
+ SessionToken: "",
+ },
+ },
+ },
+ }
+
+ creds, err := p.Retrieve()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Also check credentials
+ if creds.AccessKeyID != "AKIF" {
+ t.Fatalf("Expected 'AKIF', got %s", creds.AccessKeyID)
+ }
+ if creds.SecretAccessKey != "NOSECRET" {
+ t.Fatalf("Expected 'NOSECRET', got %s", creds.SecretAccessKey)
+ }
+ if creds.SessionToken != "" {
+ t.Fatalf("Expected empty token, got %s", creds.SessionToken)
+ }
+}
+
+func TestChainIsExpired(t *testing.T) {
+ credProvider := &credProvider{expired: true}
+ p := &Chain{
+ Providers: []Provider{
+ credProvider,
+ },
+ }
+
+ if !p.IsExpired() {
+ t.Fatal("Expected expired to be true before any Retrieve")
+ }
+
+ _, err := p.Retrieve()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if p.IsExpired() {
+ t.Fatal("Expected to be not expired after Retrieve")
+ }
+}
+
+func TestChainWithNoProvider(t *testing.T) {
+ p := &Chain{
+ Providers: []Provider{},
+ }
+ if !p.IsExpired() {
+ t.Fatal("Expected to be expired with no providers")
+ }
+ _, err := p.Retrieve()
+ if err != nil {
+ if err.Error() != "No valid providers found []" {
+ t.Error(err)
+ }
+ }
+}
+
+func TestChainProviderWithNoValidProvider(t *testing.T) {
+ errs := []error{
+ errors.New("FirstError"),
+ errors.New("SecondError"),
+ }
+ p := &Chain{
+ Providers: []Provider{
+ &credProvider{err: errs[0]},
+ &credProvider{err: errs[1]},
+ },
+ }
+
+ if !p.IsExpired() {
+ t.Fatal("Expected to be expired with no providers")
+ }
+
+ _, err := p.Retrieve()
+ if err != nil {
+ if err.Error() != "No valid providers found [FirstError SecondError]" {
+ t.Error(err)
+ }
+ }
+}
diff --git a/vendor/github.com/minio/minio-go/pkg/credentials/config.json.sample b/vendor/github.com/minio/minio-go/pkg/credentials/config.json.sample
new file mode 100644
index 000000000..130746f4b
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/pkg/credentials/config.json.sample
@@ -0,0 +1,17 @@
+{
+ "version": "8",
+ "hosts": {
+ "play": {
+ "url": "https://play.minio.io:9000",
+ "accessKey": "Q3AM3UQ867SPQQA43P2F",
+ "secretKey": "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG",
+ "api": "S3v2"
+ },
+ "s3": {
+ "url": "https://s3.amazonaws.com",
+ "accessKey": "accessKey",
+ "secretKey": "secret",
+ "api": "S3v4"
+ }
+ }
+} \ No newline at end of file
diff --git a/vendor/github.com/minio/minio-go/pkg/credentials/credentials.go b/vendor/github.com/minio/minio-go/pkg/credentials/credentials.go
new file mode 100644
index 000000000..cc3000532
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/pkg/credentials/credentials.go
@@ -0,0 +1,175 @@
+/*
+ * Minio Go Library for Amazon S3 Compatible Cloud Storage
+ * (C) 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 credentials
+
+import (
+ "sync"
+ "time"
+)
+
+// A Value is the AWS credentials value for individual credential fields.
+type Value struct {
+ // AWS Access key ID
+ AccessKeyID string
+
+ // AWS Secret Access Key
+ SecretAccessKey string
+
+ // AWS Session Token
+ SessionToken string
+
+ // Signature Type.
+ SignerType SignatureType
+}
+
+// A Provider is the interface for any component which will provide credentials
+// Value. A provider is required to manage its own Expired state, and what to
+// be expired means.
+type Provider interface {
+ // Retrieve returns nil if it successfully retrieved the value.
+ // Error is returned if the value were not obtainable, or empty.
+ Retrieve() (Value, error)
+
+ // IsExpired returns if the credentials are no longer valid, and need
+ // to be retrieved.
+ IsExpired() bool
+}
+
+// A Expiry provides shared expiration logic to be used by credentials
+// providers to implement expiry functionality.
+//
+// The best method to use this struct is as an anonymous field within the
+// provider's struct.
+//
+// Example:
+// type IAMCredentialProvider struct {
+// Expiry
+// ...
+// }
+type Expiry struct {
+ // The date/time when to expire on
+ expiration time.Time
+
+ // If set will be used by IsExpired to determine the current time.
+ // Defaults to time.Now if CurrentTime is not set.
+ CurrentTime func() time.Time
+}
+
+// SetExpiration sets the expiration IsExpired will check when called.
+//
+// If window is greater than 0 the expiration time will be reduced by the
+// window value.
+//
+// Using a window is helpful to trigger credentials to expire sooner than
+// the expiration time given to ensure no requests are made with expired
+// tokens.
+func (e *Expiry) SetExpiration(expiration time.Time, window time.Duration) {
+ e.expiration = expiration
+ if window > 0 {
+ e.expiration = e.expiration.Add(-window)
+ }
+}
+
+// IsExpired returns if the credentials are expired.
+func (e *Expiry) IsExpired() bool {
+ if e.CurrentTime == nil {
+ e.CurrentTime = time.Now
+ }
+ return e.expiration.Before(e.CurrentTime())
+}
+
+// Credentials - A container for synchronous safe retrieval of credentials Value.
+// Credentials will cache the credentials value until they expire. Once the value
+// expires the next Get will attempt to retrieve valid credentials.
+//
+// Credentials is safe to use across multiple goroutines and will manage the
+// synchronous state so the Providers do not need to implement their own
+// synchronization.
+//
+// The first Credentials.Get() will always call Provider.Retrieve() to get the
+// first instance of the credentials Value. All calls to Get() after that
+// will return the cached credentials Value until IsExpired() returns true.
+type Credentials struct {
+ sync.Mutex
+
+ creds Value
+ forceRefresh bool
+ provider Provider
+}
+
+// New returns a pointer to a new Credentials with the provider set.
+func New(provider Provider) *Credentials {
+ return &Credentials{
+ provider: provider,
+ forceRefresh: true,
+ }
+}
+
+// Get returns the credentials value, or error if the credentials Value failed
+// to be retrieved.
+//
+// Will return the cached credentials Value if it has not expired. If the
+// credentials Value has expired the Provider's Retrieve() will be called
+// to refresh the credentials.
+//
+// If Credentials.Expire() was called the credentials Value will be force
+// expired, and the next call to Get() will cause them to be refreshed.
+func (c *Credentials) Get() (Value, error) {
+ c.Lock()
+ defer c.Unlock()
+
+ if c.isExpired() {
+ creds, err := c.provider.Retrieve()
+ if err != nil {
+ return Value{}, err
+ }
+ c.creds = creds
+ c.forceRefresh = false
+ }
+
+ return c.creds, nil
+}
+
+// Expire expires the credentials and forces them to be retrieved on the
+// next call to Get().
+//
+// This will override the Provider's expired state, and force Credentials
+// to call the Provider's Retrieve().
+func (c *Credentials) Expire() {
+ c.Lock()
+ defer c.Unlock()
+
+ c.forceRefresh = true
+}
+
+// IsExpired returns if the credentials are no longer valid, and need
+// to be refreshed.
+//
+// If the Credentials were forced to be expired with Expire() this will
+// reflect that override.
+func (c *Credentials) IsExpired() bool {
+ c.Lock()
+ defer c.Unlock()
+
+ return c.isExpired()
+}
+
+// isExpired helper method wrapping the definition of expired credentials.
+func (c *Credentials) isExpired() bool {
+ return c.forceRefresh || c.provider.IsExpired()
+}
diff --git a/vendor/github.com/minio/minio-go/pkg/credentials/credentials.sample b/vendor/github.com/minio/minio-go/pkg/credentials/credentials.sample
new file mode 100644
index 000000000..7fc91d9d2
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/pkg/credentials/credentials.sample
@@ -0,0 +1,12 @@
+[default]
+aws_access_key_id = accessKey
+aws_secret_access_key = secret
+aws_session_token = token
+
+[no_token]
+aws_access_key_id = accessKey
+aws_secret_access_key = secret
+
+[with_colon]
+aws_access_key_id: accessKey
+aws_secret_access_key: secret
diff --git a/vendor/github.com/minio/minio-go/pkg/credentials/credentials_test.go b/vendor/github.com/minio/minio-go/pkg/credentials/credentials_test.go
new file mode 100644
index 000000000..cbfb673b7
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/pkg/credentials/credentials_test.go
@@ -0,0 +1,73 @@
+/*
+ * Minio Go Library for Amazon S3 Compatible Cloud Storage
+ * (C) 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 credentials
+
+import (
+ "errors"
+ "testing"
+)
+
+type credProvider struct {
+ creds Value
+ expired bool
+ err error
+}
+
+func (s *credProvider) Retrieve() (Value, error) {
+ s.expired = false
+ return s.creds, s.err
+}
+func (s *credProvider) IsExpired() bool {
+ return s.expired
+}
+
+func TestCredentialsGet(t *testing.T) {
+ c := New(&credProvider{
+ creds: Value{
+ AccessKeyID: "UXHW",
+ SecretAccessKey: "MYSECRET",
+ SessionToken: "",
+ },
+ expired: true,
+ })
+
+ creds, err := c.Get()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if "UXHW" != creds.AccessKeyID {
+ t.Errorf("Expected \"UXHW\", got %s", creds.AccessKeyID)
+ }
+ if "MYSECRET" != creds.SecretAccessKey {
+ t.Errorf("Expected \"MYSECRET\", got %s", creds.SecretAccessKey)
+ }
+ if creds.SessionToken != "" {
+ t.Errorf("Expected session token to be empty, got %s", creds.SessionToken)
+ }
+}
+
+func TestCredentialsGetWithError(t *testing.T) {
+ c := New(&credProvider{err: errors.New("Custom error")})
+
+ _, err := c.Get()
+ if err != nil {
+ if err.Error() != "Custom error" {
+ t.Errorf("Expected \"Custom error\", got %s", err.Error())
+ }
+ }
+}
diff --git a/vendor/github.com/minio/minio-go/pkg/credentials/doc.go b/vendor/github.com/minio/minio-go/pkg/credentials/doc.go
new file mode 100644
index 000000000..fa1908aeb
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/pkg/credentials/doc.go
@@ -0,0 +1,45 @@
+// Package credentials provides credential retrieval and management
+// for S3 compatible object storage.
+//
+// By default the Credentials.Get() will cache the successful result of a
+// Provider's Retrieve() until Provider.IsExpired() returns true. At which
+// point Credentials will call Provider's Retrieve() to get new credential Value.
+//
+// The Provider is responsible for determining when credentials have expired.
+// It is also important to note that Credentials will always call Retrieve the
+// first time Credentials.Get() is called.
+//
+// Example of using the environment variable credentials.
+//
+// creds := NewFromEnv()
+// // Retrieve the credentials value
+// credValue, err := creds.Get()
+// if err != nil {
+// // handle error
+// }
+//
+// Example of forcing credentials to expire and be refreshed on the next Get().
+// This may be helpful to proactively expire credentials and refresh them sooner
+// than they would naturally expire on their own.
+//
+// creds := NewFromIAM("")
+// creds.Expire()
+// credsValue, err := creds.Get()
+// // New credentials will be retrieved instead of from cache.
+//
+//
+// Custom Provider
+//
+// Each Provider built into this package also provides a helper method to generate
+// a Credentials pointer setup with the provider. To use a custom Provider just
+// create a type which satisfies the Provider interface and pass it to the
+// NewCredentials method.
+//
+// type MyProvider struct{}
+// func (m *MyProvider) Retrieve() (Value, error) {...}
+// func (m *MyProvider) IsExpired() bool {...}
+//
+// creds := NewCredentials(&MyProvider{})
+// credValue, err := creds.Get()
+//
+package credentials
diff --git a/vendor/github.com/minio/minio-go/pkg/credentials/env_aws.go b/vendor/github.com/minio/minio-go/pkg/credentials/env_aws.go
new file mode 100644
index 000000000..11934433c
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/pkg/credentials/env_aws.go
@@ -0,0 +1,71 @@
+/*
+ * Minio Go Library for Amazon S3 Compatible Cloud Storage
+ * (C) 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 credentials
+
+import "os"
+
+// A EnvAWS retrieves credentials from the environment variables of the
+// running process. EnvAWSironment credentials never expire.
+//
+// EnvAWSironment variables used:
+//
+// * Access Key ID: AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY.
+// * Secret Access Key: AWS_SECRET_ACCESS_KEY or AWS_SECRET_KEY.
+// * Secret Token: AWS_SESSION_TOKEN.
+type EnvAWS struct {
+ retrieved bool
+}
+
+// NewEnvAWS returns a pointer to a new Credentials object
+// wrapping the environment variable provider.
+func NewEnvAWS() *Credentials {
+ return New(&EnvAWS{})
+}
+
+// Retrieve retrieves the keys from the environment.
+func (e *EnvAWS) Retrieve() (Value, error) {
+ e.retrieved = false
+
+ id := os.Getenv("AWS_ACCESS_KEY_ID")
+ if id == "" {
+ id = os.Getenv("AWS_ACCESS_KEY")
+ }
+
+ secret := os.Getenv("AWS_SECRET_ACCESS_KEY")
+ if secret == "" {
+ secret = os.Getenv("AWS_SECRET_KEY")
+ }
+
+ signerType := SignatureV4
+ if id == "" || secret == "" {
+ signerType = SignatureAnonymous
+ }
+
+ e.retrieved = true
+ return Value{
+ AccessKeyID: id,
+ SecretAccessKey: secret,
+ SessionToken: os.Getenv("AWS_SESSION_TOKEN"),
+ SignerType: signerType,
+ }, nil
+}
+
+// IsExpired returns if the credentials have been retrieved.
+func (e *EnvAWS) IsExpired() bool {
+ return !e.retrieved
+}
diff --git a/vendor/github.com/minio/minio-go/pkg/credentials/env_minio.go b/vendor/github.com/minio/minio-go/pkg/credentials/env_minio.go
new file mode 100644
index 000000000..791087ef5
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/pkg/credentials/env_minio.go
@@ -0,0 +1,62 @@
+/*
+ * Minio Go Library for Amazon S3 Compatible Cloud Storage
+ * (C) 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 credentials
+
+import "os"
+
+// A EnvMinio retrieves credentials from the environment variables of the
+// running process. EnvMinioironment credentials never expire.
+//
+// EnvMinioironment variables used:
+//
+// * Access Key ID: MINIO_ACCESS_KEY.
+// * Secret Access Key: MINIO_SECRET_KEY.
+type EnvMinio struct {
+ retrieved bool
+}
+
+// NewEnvMinio returns a pointer to a new Credentials object
+// wrapping the environment variable provider.
+func NewEnvMinio() *Credentials {
+ return New(&EnvMinio{})
+}
+
+// Retrieve retrieves the keys from the environment.
+func (e *EnvMinio) Retrieve() (Value, error) {
+ e.retrieved = false
+
+ id := os.Getenv("MINIO_ACCESS_KEY")
+ secret := os.Getenv("MINIO_SECRET_KEY")
+
+ signerType := SignatureV4
+ if id == "" || secret == "" {
+ signerType = SignatureAnonymous
+ }
+
+ e.retrieved = true
+ return Value{
+ AccessKeyID: id,
+ SecretAccessKey: secret,
+ SignerType: signerType,
+ }, nil
+}
+
+// IsExpired returns if the credentials have been retrieved.
+func (e *EnvMinio) IsExpired() bool {
+ return !e.retrieved
+}
diff --git a/vendor/github.com/minio/minio-go/pkg/credentials/env_test.go b/vendor/github.com/minio/minio-go/pkg/credentials/env_test.go
new file mode 100644
index 000000000..2f72bea40
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/pkg/credentials/env_test.go
@@ -0,0 +1,105 @@
+/*
+ * Minio Go Library for Amazon S3 Compatible Cloud Storage
+ * (C) 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 credentials
+
+import (
+ "os"
+ "reflect"
+ "testing"
+)
+
+func TestEnvAWSRetrieve(t *testing.T) {
+ os.Clearenv()
+ os.Setenv("AWS_ACCESS_KEY_ID", "access")
+ os.Setenv("AWS_SECRET_ACCESS_KEY", "secret")
+ os.Setenv("AWS_SESSION_TOKEN", "token")
+
+ e := EnvAWS{}
+ if !e.IsExpired() {
+ t.Error("Expect creds to be expired before retrieve.")
+ }
+
+ creds, err := e.Retrieve()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ expectedCreds := Value{
+ AccessKeyID: "access",
+ SecretAccessKey: "secret",
+ SessionToken: "token",
+ SignerType: SignatureV4,
+ }
+ if !reflect.DeepEqual(creds, expectedCreds) {
+ t.Errorf("Expected %v, got %v", expectedCreds, creds)
+ }
+
+ if e.IsExpired() {
+ t.Error("Expect creds to not be expired after retrieve.")
+ }
+
+ os.Clearenv()
+ os.Setenv("AWS_ACCESS_KEY", "access")
+ os.Setenv("AWS_SECRET_KEY", "secret")
+
+ expectedCreds = Value{
+ AccessKeyID: "access",
+ SecretAccessKey: "secret",
+ SignerType: SignatureV4,
+ }
+
+ creds, err = e.Retrieve()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if !reflect.DeepEqual(creds, expectedCreds) {
+ t.Errorf("Expected %v, got %v", expectedCreds, creds)
+ }
+
+}
+
+func TestEnvMinioRetrieve(t *testing.T) {
+ os.Clearenv()
+
+ os.Setenv("MINIO_ACCESS_KEY", "access")
+ os.Setenv("MINIO_SECRET_KEY", "secret")
+
+ e := EnvMinio{}
+ if !e.IsExpired() {
+ t.Error("Expect creds to be expired before retrieve.")
+ }
+
+ creds, err := e.Retrieve()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ expectedCreds := Value{
+ AccessKeyID: "access",
+ SecretAccessKey: "secret",
+ SignerType: SignatureV4,
+ }
+ if !reflect.DeepEqual(creds, expectedCreds) {
+ t.Errorf("Expected %v, got %v", expectedCreds, creds)
+ }
+
+ if e.IsExpired() {
+ t.Error("Expect creds to not be expired after retrieve.")
+ }
+}
diff --git a/vendor/github.com/minio/minio-go/pkg/credentials/file_aws_credentials.go b/vendor/github.com/minio/minio-go/pkg/credentials/file_aws_credentials.go
new file mode 100644
index 000000000..1be621385
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/pkg/credentials/file_aws_credentials.go
@@ -0,0 +1,120 @@
+/*
+ * Minio Go Library for Amazon S3 Compatible Cloud Storage
+ * (C) 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 credentials
+
+import (
+ "os"
+ "path/filepath"
+
+ "github.com/go-ini/ini"
+ homedir "github.com/minio/go-homedir"
+)
+
+// A FileAWSCredentials retrieves credentials from the current user's home
+// directory, and keeps track if those credentials are expired.
+//
+// Profile ini file example: $HOME/.aws/credentials
+type FileAWSCredentials struct {
+ // Path to the shared credentials file.
+ //
+ // If empty will look for "AWS_SHARED_CREDENTIALS_FILE" env variable. If the
+ // env value is empty will default to current user's home directory.
+ // Linux/OSX: "$HOME/.aws/credentials"
+ // Windows: "%USERPROFILE%\.aws\credentials"
+ filename string
+
+ // AWS Profile to extract credentials from the shared credentials file. If empty
+ // will default to environment variable "AWS_PROFILE" or "default" if
+ // environment variable is also not set.
+ profile string
+
+ // retrieved states if the credentials have been successfully retrieved.
+ retrieved bool
+}
+
+// NewFileAWSCredentials returns a pointer to a new Credentials object
+// wrapping the Profile file provider.
+func NewFileAWSCredentials(filename string, profile string) *Credentials {
+ return New(&FileAWSCredentials{
+ filename: filename,
+ profile: profile,
+ })
+}
+
+// Retrieve reads and extracts the shared credentials from the current
+// users home directory.
+func (p *FileAWSCredentials) Retrieve() (Value, error) {
+ if p.filename == "" {
+ p.filename = os.Getenv("AWS_SHARED_CREDENTIALS_FILE")
+ if p.filename == "" {
+ homeDir, err := homedir.Dir()
+ if err != nil {
+ return Value{}, err
+ }
+ p.filename = filepath.Join(homeDir, ".aws", "credentials")
+ }
+ }
+ if p.profile == "" {
+ p.profile = os.Getenv("AWS_PROFILE")
+ if p.profile == "" {
+ p.profile = "default"
+ }
+ }
+
+ p.retrieved = false
+
+ iniProfile, err := loadProfile(p.filename, p.profile)
+ if err != nil {
+ return Value{}, err
+ }
+
+ // Default to empty string if not found.
+ id := iniProfile.Key("aws_access_key_id")
+ // Default to empty string if not found.
+ secret := iniProfile.Key("aws_secret_access_key")
+ // Default to empty string if not found.
+ token := iniProfile.Key("aws_session_token")
+
+ p.retrieved = true
+ return Value{
+ AccessKeyID: id.String(),
+ SecretAccessKey: secret.String(),
+ SessionToken: token.String(),
+ SignerType: SignatureV4,
+ }, nil
+}
+
+// IsExpired returns if the shared credentials have expired.
+func (p *FileAWSCredentials) IsExpired() bool {
+ return !p.retrieved
+}
+
+// loadProfiles loads from the file pointed to by shared credentials filename for profile.
+// The credentials retrieved from the profile will be returned or error. Error will be
+// returned if it fails to read from the file, or the data is invalid.
+func loadProfile(filename, profile string) (*ini.Section, error) {
+ config, err := ini.Load(filename)
+ if err != nil {
+ return nil, err
+ }
+ iniProfile, err := config.GetSection(profile)
+ if err != nil {
+ return nil, err
+ }
+ return iniProfile, nil
+}
diff --git a/vendor/github.com/minio/minio-go/pkg/credentials/file_minio_client.go b/vendor/github.com/minio/minio-go/pkg/credentials/file_minio_client.go
new file mode 100644
index 000000000..9e26dd302
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/pkg/credentials/file_minio_client.go
@@ -0,0 +1,129 @@
+/*
+ * Minio Go Library for Amazon S3 Compatible Cloud Storage
+ * (C) 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 credentials
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "runtime"
+
+ homedir "github.com/minio/go-homedir"
+)
+
+// A FileMinioClient retrieves credentials from the current user's home
+// directory, and keeps track if those credentials are expired.
+//
+// Configuration file example: $HOME/.mc/config.json
+type FileMinioClient struct {
+ // Path to the shared credentials file.
+ //
+ // If empty will look for "MINIO_SHARED_CREDENTIALS_FILE" env variable. If the
+ // env value is empty will default to current user's home directory.
+ // Linux/OSX: "$HOME/.mc/config.json"
+ // Windows: "%USERALIAS%\mc\config.json"
+ filename string
+
+ // Minio Alias to extract credentials from the shared credentials file. If empty
+ // will default to environment variable "MINIO_ALIAS" or "default" if
+ // environment variable is also not set.
+ alias string
+
+ // retrieved states if the credentials have been successfully retrieved.
+ retrieved bool
+}
+
+// NewFileMinioClient returns a pointer to a new Credentials object
+// wrapping the Alias file provider.
+func NewFileMinioClient(filename string, alias string) *Credentials {
+ return New(&FileMinioClient{
+ filename: filename,
+ alias: alias,
+ })
+}
+
+// Retrieve reads and extracts the shared credentials from the current
+// users home directory.
+func (p *FileMinioClient) Retrieve() (Value, error) {
+ if p.filename == "" {
+ homeDir, err := homedir.Dir()
+ if err != nil {
+ return Value{}, err
+ }
+ p.filename = filepath.Join(homeDir, ".mc", "config.json")
+ if runtime.GOOS == "windows" {
+ p.filename = filepath.Join(homeDir, "mc", "config.json")
+ }
+ }
+
+ if p.alias == "" {
+ p.alias = os.Getenv("MINIO_ALIAS")
+ if p.alias == "" {
+ p.alias = "s3"
+ }
+ }
+
+ p.retrieved = false
+
+ hostCfg, err := loadAlias(p.filename, p.alias)
+ if err != nil {
+ return Value{}, err
+ }
+
+ p.retrieved = true
+ return Value{
+ AccessKeyID: hostCfg.AccessKey,
+ SecretAccessKey: hostCfg.SecretKey,
+ SignerType: parseSignatureType(hostCfg.API),
+ }, nil
+}
+
+// IsExpired returns if the shared credentials have expired.
+func (p *FileMinioClient) IsExpired() bool {
+ return !p.retrieved
+}
+
+// hostConfig configuration of a host.
+type hostConfig struct {
+ URL string `json:"url"`
+ AccessKey string `json:"accessKey"`
+ SecretKey string `json:"secretKey"`
+ API string `json:"api"`
+}
+
+// config config version.
+type config struct {
+ Version string `json:"version"`
+ Hosts map[string]hostConfig `json:"hosts"`
+}
+
+// loadAliass loads from the file pointed to by shared credentials filename for alias.
+// The credentials retrieved from the alias will be returned or error. Error will be
+// returned if it fails to read from the file.
+func loadAlias(filename, alias string) (hostConfig, error) {
+ cfg := &config{}
+ configBytes, err := ioutil.ReadFile(filename)
+ if err != nil {
+ return hostConfig{}, err
+ }
+ if err = json.Unmarshal(configBytes, cfg); err != nil {
+ return hostConfig{}, err
+ }
+ return cfg.Hosts[alias], nil
+}
diff --git a/vendor/github.com/minio/minio-go/pkg/credentials/file_test.go b/vendor/github.com/minio/minio-go/pkg/credentials/file_test.go
new file mode 100644
index 000000000..c62c53365
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/pkg/credentials/file_test.go
@@ -0,0 +1,189 @@
+/*
+ * Minio Go Library for Amazon S3 Compatible Cloud Storage
+ * (C) 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 credentials
+
+import (
+ "os"
+ "path/filepath"
+ "testing"
+)
+
+func TestFileAWS(t *testing.T) {
+ os.Clearenv()
+
+ creds := NewFileAWSCredentials("credentials.sample", "")
+ credValues, err := creds.Get()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if credValues.AccessKeyID != "accessKey" {
+ t.Errorf("Expected 'accessKey', got %s'", credValues.AccessKeyID)
+ }
+ if credValues.SecretAccessKey != "secret" {
+ t.Errorf("Expected 'secret', got %s'", credValues.SecretAccessKey)
+ }
+ if credValues.SessionToken != "token" {
+ t.Errorf("Expected 'token', got %s'", credValues.SessionToken)
+ }
+
+ os.Setenv("AWS_SHARED_CREDENTIALS_FILE", "credentials.sample")
+ creds = NewFileAWSCredentials("", "")
+ credValues, err = creds.Get()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if credValues.AccessKeyID != "accessKey" {
+ t.Errorf("Expected 'accessKey', got %s'", credValues.AccessKeyID)
+ }
+ if credValues.SecretAccessKey != "secret" {
+ t.Errorf("Expected 'secret', got %s'", credValues.SecretAccessKey)
+ }
+ if credValues.SessionToken != "token" {
+ t.Errorf("Expected 'token', got %s'", credValues.SessionToken)
+ }
+
+ wd, err := os.Getwd()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ os.Setenv("AWS_SHARED_CREDENTIALS_FILE", filepath.Join(wd, "credentials.sample"))
+ creds = NewFileAWSCredentials("", "")
+ credValues, err = creds.Get()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if credValues.AccessKeyID != "accessKey" {
+ t.Errorf("Expected 'accessKey', got %s'", credValues.AccessKeyID)
+ }
+ if credValues.SecretAccessKey != "secret" {
+ t.Errorf("Expected 'secret', got %s'", credValues.SecretAccessKey)
+ }
+ if credValues.SessionToken != "token" {
+ t.Errorf("Expected 'token', got %s'", credValues.SessionToken)
+ }
+
+ os.Clearenv()
+ os.Setenv("AWS_PROFILE", "no_token")
+
+ creds = NewFileAWSCredentials("credentials.sample", "")
+ credValues, err = creds.Get()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if credValues.AccessKeyID != "accessKey" {
+ t.Errorf("Expected 'accessKey', got %s'", credValues.AccessKeyID)
+ }
+ if credValues.SecretAccessKey != "secret" {
+ t.Errorf("Expected 'secret', got %s'", credValues.SecretAccessKey)
+ }
+
+ os.Clearenv()
+
+ creds = NewFileAWSCredentials("credentials.sample", "no_token")
+ credValues, err = creds.Get()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if credValues.AccessKeyID != "accessKey" {
+ t.Errorf("Expected 'accessKey', got %s'", credValues.AccessKeyID)
+ }
+ if credValues.SecretAccessKey != "secret" {
+ t.Errorf("Expected 'secret', got %s'", credValues.SecretAccessKey)
+ }
+
+ creds = NewFileAWSCredentials("credentials-non-existent.sample", "no_token")
+ _, err = creds.Get()
+ if !os.IsNotExist(err) {
+ t.Errorf("Expected open non-existent.json: no such file or directory, got %s", err)
+ }
+ if !creds.IsExpired() {
+ t.Error("Should be expired if not loaded")
+ }
+}
+
+func TestFileMinioClient(t *testing.T) {
+ os.Clearenv()
+
+ creds := NewFileMinioClient("config.json.sample", "")
+ credValues, err := creds.Get()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if credValues.AccessKeyID != "accessKey" {
+ t.Errorf("Expected 'accessKey', got %s'", credValues.AccessKeyID)
+ }
+ if credValues.SecretAccessKey != "secret" {
+ t.Errorf("Expected 'secret', got %s'", credValues.SecretAccessKey)
+ }
+ if credValues.SignerType != SignatureV4 {
+ t.Errorf("Expected 'S3v4', got %s'", credValues.SignerType)
+ }
+
+ os.Clearenv()
+ os.Setenv("MINIO_ALIAS", "play")
+
+ creds = NewFileMinioClient("config.json.sample", "")
+ credValues, err = creds.Get()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if credValues.AccessKeyID != "Q3AM3UQ867SPQQA43P2F" {
+ t.Errorf("Expected 'Q3AM3UQ867SPQQA43P2F', got %s'", credValues.AccessKeyID)
+ }
+ if credValues.SecretAccessKey != "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG" {
+ t.Errorf("Expected 'zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG', got %s'", credValues.SecretAccessKey)
+ }
+ if credValues.SignerType != SignatureV2 {
+ t.Errorf("Expected 'S3v2', got %s'", credValues.SignerType)
+ }
+
+ os.Clearenv()
+
+ creds = NewFileMinioClient("config.json.sample", "play")
+ credValues, err = creds.Get()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if credValues.AccessKeyID != "Q3AM3UQ867SPQQA43P2F" {
+ t.Errorf("Expected 'Q3AM3UQ867SPQQA43P2F', got %s'", credValues.AccessKeyID)
+ }
+ if credValues.SecretAccessKey != "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG" {
+ t.Errorf("Expected 'zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG', got %s'", credValues.SecretAccessKey)
+ }
+ if credValues.SignerType != SignatureV2 {
+ t.Errorf("Expected 'S3v2', got %s'", credValues.SignerType)
+ }
+
+ creds = NewFileMinioClient("non-existent.json", "play")
+ _, err = creds.Get()
+ if !os.IsNotExist(err) {
+ t.Errorf("Expected open non-existent.json: no such file or directory, got %s", err)
+ }
+ if !creds.IsExpired() {
+ t.Error("Should be expired if not loaded")
+ }
+}
diff --git a/vendor/github.com/minio/minio-go/pkg/credentials/iam_aws.go b/vendor/github.com/minio/minio-go/pkg/credentials/iam_aws.go
new file mode 100644
index 000000000..ee24a213b
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/pkg/credentials/iam_aws.go
@@ -0,0 +1,196 @@
+/*
+ * Minio Go Library for Amazon S3 Compatible Cloud Storage
+ * (C) 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 credentials
+
+import (
+ "bufio"
+ "encoding/json"
+ "errors"
+ "net/http"
+ "net/url"
+ "path"
+ "time"
+)
+
+// DefaultExpiryWindow - Default expiry window.
+// ExpiryWindow will allow the credentials to trigger refreshing
+// prior to the credentials actually expiring. This is beneficial
+// so race conditions with expiring credentials do not cause
+// request to fail unexpectedly due to ExpiredTokenException exceptions.
+const DefaultExpiryWindow = time.Second * 10 // 10 secs
+
+// A IAM retrieves credentials from the EC2 service, and keeps track if
+// those credentials are expired.
+type IAM struct {
+ Expiry
+
+ // Required http Client to use when connecting to IAM metadata service.
+ Client *http.Client
+
+ // Custom endpoint in place of
+ endpoint string
+}
+
+// redirectHeaders copies all headers when following a redirect URL.
+// This won't be needed anymore from go 1.8 (https://github.com/golang/go/issues/4800)
+func redirectHeaders(req *http.Request, via []*http.Request) error {
+ if len(via) == 0 {
+ return nil
+ }
+ for key, val := range via[0].Header {
+ req.Header[key] = val
+ }
+ return nil
+}
+
+// NewIAM returns a pointer to a new Credentials object wrapping
+// the IAM. Takes a ConfigProvider to create a EC2Metadata client.
+// The ConfigProvider is satisfied by the session.Session type.
+func NewIAM(endpoint string) *Credentials {
+ if endpoint == "" {
+ // IAM Roles for Amazon EC2
+ // http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html
+ endpoint = "http://169.254.169.254"
+ }
+ p := &IAM{
+ Client: &http.Client{
+ Transport: http.DefaultTransport,
+ CheckRedirect: redirectHeaders,
+ },
+ endpoint: endpoint,
+ }
+ return New(p)
+}
+
+// Retrieve retrieves credentials from the EC2 service.
+// Error will be returned if the request fails, or unable to extract
+// the desired
+func (m *IAM) Retrieve() (Value, error) {
+ credsList, err := requestCredList(m.Client, m.endpoint)
+ if err != nil {
+ return Value{}, err
+ }
+
+ if len(credsList) == 0 {
+ return Value{}, errors.New("empty EC2 Role list")
+ }
+ credsName := credsList[0]
+
+ roleCreds, err := requestCred(m.Client, m.endpoint, credsName)
+ if err != nil {
+ return Value{}, err
+ }
+
+ // Expiry window is set to 10secs.
+ m.SetExpiration(roleCreds.Expiration, DefaultExpiryWindow)
+
+ return Value{
+ AccessKeyID: roleCreds.AccessKeyID,
+ SecretAccessKey: roleCreds.SecretAccessKey,
+ SessionToken: roleCreds.Token,
+ SignerType: SignatureV4,
+ }, nil
+}
+
+// A ec2RoleCredRespBody provides the shape for unmarshaling credential
+// request responses.
+type ec2RoleCredRespBody struct {
+ // Success State
+ Expiration time.Time
+ AccessKeyID string
+ SecretAccessKey string
+ Token string
+
+ // Error state
+ Code string
+ Message string
+}
+
+const iamSecurityCredsPath = "/latest/meta-data/iam/security-credentials"
+
+// requestCredList requests a list of credentials from the EC2 service.
+// If there are no credentials, or there is an error making or receiving the request
+func requestCredList(client *http.Client, endpoint string) ([]string, error) {
+ u, err := url.Parse(endpoint)
+ if err != nil {
+ return nil, err
+ }
+ u.Path = iamSecurityCredsPath
+ req, err := http.NewRequest("GET", u.String(), nil)
+ if err != nil {
+ return nil, err
+ }
+ resp, err := client.Do(req)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+ if resp.StatusCode != http.StatusOK {
+ return nil, errors.New(resp.Status)
+ }
+
+ credsList := []string{}
+ s := bufio.NewScanner(resp.Body)
+ for s.Scan() {
+ credsList = append(credsList, s.Text())
+ }
+
+ if err := s.Err(); err != nil {
+ return nil, err
+ }
+
+ return credsList, nil
+}
+
+// requestCred requests the credentials for a specific credentials from the EC2 service.
+//
+// If the credentials cannot be found, or there is an error reading the response
+// and error will be returned.
+func requestCred(client *http.Client, endpoint string, credsName string) (ec2RoleCredRespBody, error) {
+ u, err := url.Parse(endpoint)
+ if err != nil {
+ return ec2RoleCredRespBody{}, err
+ }
+
+ u.Path = path.Join(iamSecurityCredsPath, credsName)
+ req, err := http.NewRequest("GET", u.String(), nil)
+ if err != nil {
+ return ec2RoleCredRespBody{}, err
+ }
+
+ resp, err := client.Do(req)
+ if err != nil {
+ return ec2RoleCredRespBody{}, err
+ }
+ defer resp.Body.Close()
+ if resp.StatusCode != http.StatusOK {
+ return ec2RoleCredRespBody{}, errors.New(resp.Status)
+ }
+
+ respCreds := ec2RoleCredRespBody{}
+ if err := json.NewDecoder(resp.Body).Decode(&respCreds); err != nil {
+ return ec2RoleCredRespBody{}, err
+ }
+
+ if respCreds.Code != "Success" {
+ // If an error code was returned something failed requesting the role.
+ return ec2RoleCredRespBody{}, errors.New(respCreds.Message)
+ }
+
+ return respCreds, nil
+}
diff --git a/vendor/github.com/minio/minio-go/pkg/credentials/iam_aws_test.go b/vendor/github.com/minio/minio-go/pkg/credentials/iam_aws_test.go
new file mode 100644
index 000000000..19553945d
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/pkg/credentials/iam_aws_test.go
@@ -0,0 +1,180 @@
+package credentials
+
+import (
+ "fmt"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+ "time"
+)
+
+const credsRespTmpl = `{
+ "Code": "Success",
+ "Type": "AWS-HMAC",
+ "AccessKeyId" : "accessKey",
+ "SecretAccessKey" : "secret",
+ "Token" : "token",
+ "Expiration" : "%s",
+ "LastUpdated" : "2009-11-23T0:00:00Z"
+}`
+
+const credsFailRespTmpl = `{
+ "Code": "ErrorCode",
+ "Message": "ErrorMsg",
+ "LastUpdated": "2009-11-23T0:00:00Z"
+}`
+
+func initTestFailServer() *httptest.Server {
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ http.Error(w, "Not allowed", http.StatusBadRequest)
+ }))
+ return server
+}
+
+func initTestServerNoRoles() *httptest.Server {
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Write([]byte(""))
+ }))
+ return server
+}
+
+func initTestServer(expireOn string, failAssume bool) *httptest.Server {
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Path == "/latest/meta-data/iam/security-credentials" {
+ fmt.Fprintln(w, "RoleName")
+ } else if r.URL.Path == "/latest/meta-data/iam/security-credentials/RoleName" {
+ if failAssume {
+ fmt.Fprintf(w, credsFailRespTmpl)
+ } else {
+ fmt.Fprintf(w, credsRespTmpl, expireOn)
+ }
+ } else {
+ http.Error(w, "bad request", http.StatusBadRequest)
+ }
+ }))
+
+ return server
+}
+
+func TestIAMMalformedEndpoint(t *testing.T) {
+ creds := NewIAM("%%%%")
+ _, err := creds.Get()
+ if err == nil {
+ t.Fatal("Unexpected should fail here")
+ }
+ if err.Error() != `parse %%%%: invalid URL escape "%%%"` {
+ t.Fatalf("Expected parse %%%%%%%%: invalid URL escape \"%%%%%%\", got %s", err)
+ }
+}
+
+func TestIAMFailServer(t *testing.T) {
+ server := initTestFailServer()
+ defer server.Close()
+
+ creds := NewIAM(server.URL)
+
+ _, err := creds.Get()
+ if err == nil {
+ t.Fatal("Unexpected should fail here")
+ }
+ if err.Error() != "400 Bad Request" {
+ t.Fatalf("Expected '400 Bad Request', got %s", err)
+ }
+}
+
+func TestIAMNoRoles(t *testing.T) {
+ server := initTestServerNoRoles()
+ defer server.Close()
+
+ creds := NewIAM(server.URL)
+ _, err := creds.Get()
+ if err == nil {
+ t.Fatal("Unexpected should fail here")
+ }
+ if err.Error() != "empty EC2 Role list" {
+ t.Fatalf("Expected 'empty EC2 Role list', got %s", err)
+ }
+}
+
+func TestIAM(t *testing.T) {
+ server := initTestServer("2014-12-16T01:51:37Z", false)
+ defer server.Close()
+
+ p := &IAM{
+ Client: http.DefaultClient,
+ endpoint: server.URL,
+ }
+
+ creds, err := p.Retrieve()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if "accessKey" != creds.AccessKeyID {
+ t.Errorf("Expected \"accessKey\", got %s", creds.AccessKeyID)
+ }
+
+ if "secret" != creds.SecretAccessKey {
+ t.Errorf("Expected \"secret\", got %s", creds.SecretAccessKey)
+ }
+
+ if "token" != creds.SessionToken {
+ t.Errorf("Expected \"token\", got %s", creds.SessionToken)
+ }
+
+ if !p.IsExpired() {
+ t.Error("Expected creds to be expired.")
+ }
+}
+
+func TestIAMFailAssume(t *testing.T) {
+ server := initTestServer("2014-12-16T01:51:37Z", true)
+ defer server.Close()
+
+ p := &IAM{
+ Client: http.DefaultClient,
+ endpoint: server.URL,
+ }
+
+ _, err := p.Retrieve()
+ if err == nil {
+ t.Fatal("Unexpected success, should fail")
+ }
+ if err.Error() != "ErrorMsg" {
+ t.Errorf("Expected \"ErrorMsg\", got %s", err)
+ }
+}
+
+func TestIAMIsExpired(t *testing.T) {
+ server := initTestServer("2014-12-16T01:51:37Z", false)
+ defer server.Close()
+
+ p := &IAM{
+ Client: http.DefaultClient,
+ endpoint: server.URL,
+ }
+ p.CurrentTime = func() time.Time {
+ return time.Date(2014, 12, 15, 21, 26, 0, 0, time.UTC)
+ }
+
+ if !p.IsExpired() {
+ t.Error("Expected creds to be expired before retrieve.")
+ }
+
+ _, err := p.Retrieve()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if p.IsExpired() {
+ t.Error("Expected creds to not be expired after retrieve.")
+ }
+
+ p.CurrentTime = func() time.Time {
+ return time.Date(3014, 12, 15, 21, 26, 0, 0, time.UTC)
+ }
+
+ if !p.IsExpired() {
+ t.Error("Expected creds to be expired when curren time has changed")
+ }
+}
diff --git a/vendor/github.com/minio/minio-go/pkg/credentials/signature-type.go b/vendor/github.com/minio/minio-go/pkg/credentials/signature-type.go
new file mode 100644
index 000000000..c64ad6c23
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/pkg/credentials/signature-type.go
@@ -0,0 +1,76 @@
+/*
+ * Minio Go Library for Amazon S3 Compatible Cloud Storage (C) 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 credentials
+
+import "strings"
+
+// SignatureType is type of Authorization requested for a given HTTP request.
+type SignatureType int
+
+// Different types of supported signatures - default is SignatureV4 or SignatureDefault.
+const (
+ // SignatureDefault is always set to v4.
+ SignatureDefault SignatureType = iota
+ SignatureV4
+ SignatureV2
+ SignatureV4Streaming
+ SignatureAnonymous // Anonymous signature signifies, no signature.
+)
+
+// IsV2 - is signature SignatureV2?
+func (s SignatureType) IsV2() bool {
+ return s == SignatureV2
+}
+
+// IsV4 - is signature SignatureV4?
+func (s SignatureType) IsV4() bool {
+ return s == SignatureV4 || s == SignatureDefault
+}
+
+// IsStreamingV4 - is signature SignatureV4Streaming?
+func (s SignatureType) IsStreamingV4() bool {
+ return s == SignatureV4Streaming
+}
+
+// IsAnonymous - is signature empty?
+func (s SignatureType) IsAnonymous() bool {
+ return s == SignatureAnonymous
+}
+
+// Stringer humanized version of signature type,
+// strings returned here are case insensitive.
+func (s SignatureType) String() string {
+ if s.IsV2() {
+ return "S3v2"
+ } else if s.IsV4() {
+ return "S3v4"
+ } else if s.IsStreamingV4() {
+ return "S3v4Streaming"
+ }
+ return "Anonymous"
+}
+
+func parseSignatureType(str string) SignatureType {
+ if strings.EqualFold(str, "S3v4") {
+ return SignatureV4
+ } else if strings.EqualFold(str, "S3v2") {
+ return SignatureV2
+ } else if strings.EqualFold(str, "S3v4Streaming") {
+ return SignatureV4Streaming
+ }
+ return SignatureAnonymous
+}
diff --git a/vendor/github.com/minio/minio-go/pkg/credentials/static.go b/vendor/github.com/minio/minio-go/pkg/credentials/static.go
new file mode 100644
index 000000000..25aff5696
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/pkg/credentials/static.go
@@ -0,0 +1,67 @@
+/*
+ * Minio Go Library for Amazon S3 Compatible Cloud Storage
+ * (C) 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 credentials
+
+// A Static is a set of credentials which are set programmatically,
+// and will never expire.
+type Static struct {
+ Value
+}
+
+// NewStaticV2 returns a pointer to a new Credentials object
+// wrapping a static credentials value provider, signature is
+// set to v2. If access and secret are not specified then
+// regardless of signature type set it Value will return
+// as anonymous.
+func NewStaticV2(id, secret, token string) *Credentials {
+ return NewStatic(id, secret, token, SignatureV2)
+}
+
+// NewStaticV4 is similar to NewStaticV2 with similar considerations.
+func NewStaticV4(id, secret, token string) *Credentials {
+ return NewStatic(id, secret, token, SignatureV4)
+}
+
+// NewStatic returns a pointer to a new Credentials object
+// wrapping a static credentials value provider.
+func NewStatic(id, secret, token string, signerType SignatureType) *Credentials {
+ return New(&Static{
+ Value: Value{
+ AccessKeyID: id,
+ SecretAccessKey: secret,
+ SessionToken: token,
+ SignerType: signerType,
+ },
+ })
+}
+
+// Retrieve returns the static credentials.
+func (s *Static) Retrieve() (Value, error) {
+ if s.AccessKeyID == "" || s.SecretAccessKey == "" {
+ // Anonymous is not an error
+ return Value{SignerType: SignatureAnonymous}, nil
+ }
+ return s.Value, nil
+}
+
+// IsExpired returns if the credentials are expired.
+//
+// For Static, the credentials never expired.
+func (s *Static) IsExpired() bool {
+ return false
+}
diff --git a/vendor/github.com/minio/minio-go/pkg/credentials/static_test.go b/vendor/github.com/minio/minio-go/pkg/credentials/static_test.go
new file mode 100644
index 000000000..491b1554b
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/pkg/credentials/static_test.go
@@ -0,0 +1,68 @@
+/*
+ * Minio Go Library for Amazon S3 Compatible Cloud Storage
+ * (C) 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 credentials
+
+import "testing"
+
+func TestStaticGet(t *testing.T) {
+ creds := NewStatic("UXHW", "SECRET", "", SignatureV4)
+ credValues, err := creds.Get()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if "UXHW" != credValues.AccessKeyID {
+ t.Errorf("Expected access key ID to match \"UXHW\", got %s", credValues.AccessKeyID)
+ }
+ if "SECRET" != credValues.SecretAccessKey {
+ t.Errorf("Expected secret access key to match \"SECRET\", got %s", credValues.SecretAccessKey)
+ }
+
+ if credValues.SessionToken != "" {
+ t.Error("Expected session token to match")
+ }
+
+ if credValues.SignerType != SignatureV4 {
+ t.Errorf("Expected 'S3v4', got %s", credValues.SignerType)
+ }
+
+ if creds.IsExpired() {
+ t.Error("Static credentials should never expire")
+ }
+
+ creds = NewStatic("", "", "", SignatureDefault)
+ credValues, err = creds.Get()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if "" != credValues.AccessKeyID {
+ t.Errorf("Expected access key ID to match empty string, got %s", credValues.AccessKeyID)
+ }
+ if "" != credValues.SecretAccessKey {
+ t.Errorf("Expected secret access key to match empty string, got %s", credValues.SecretAccessKey)
+ }
+
+ if !credValues.SignerType.IsAnonymous() {
+ t.Errorf("Expected 'Anonymous', got %s", credValues.SignerType)
+ }
+
+ if creds.IsExpired() {
+ t.Error("Static credentials should never expire")
+ }
+}
diff --git a/vendor/github.com/minio/minio-go/pkg/encrypt/cbc.go b/vendor/github.com/minio/minio-go/pkg/encrypt/cbc.go
new file mode 100644
index 000000000..7670e68f4
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/pkg/encrypt/cbc.go
@@ -0,0 +1,284 @@
+/*
+ * Minio Go Library for Amazon S3 Compatible Cloud Storage (C) 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 encrypt
+
+import (
+ "bytes"
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/rand"
+ "encoding/base64"
+ "errors"
+ "io"
+)
+
+// Crypt mode - encryption or decryption
+type cryptMode int
+
+const (
+ encryptMode cryptMode = iota
+ decryptMode
+)
+
+// CBCSecureMaterials encrypts/decrypts data using AES CBC algorithm
+type CBCSecureMaterials struct {
+
+ // Data stream to encrypt/decrypt
+ stream io.Reader
+
+ // Last internal error
+ err error
+
+ // End of file reached
+ eof bool
+
+ // Holds initial data
+ srcBuf *bytes.Buffer
+
+ // Holds transformed data (encrypted or decrypted)
+ dstBuf *bytes.Buffer
+
+ // Encryption algorithm
+ encryptionKey Key
+
+ // Key to encrypts/decrypts data
+ contentKey []byte
+
+ // Encrypted form of contentKey
+ cryptedKey []byte
+
+ // Initialization vector
+ iv []byte
+
+ // matDesc - currently unused
+ matDesc []byte
+
+ // Indicate if we are going to encrypt or decrypt
+ cryptMode cryptMode
+
+ // Helper that encrypts/decrypts data
+ blockMode cipher.BlockMode
+}
+
+// NewCBCSecureMaterials builds new CBC crypter module with
+// the specified encryption key (symmetric or asymmetric)
+func NewCBCSecureMaterials(key Key) (*CBCSecureMaterials, error) {
+ if key == nil {
+ return nil, errors.New("Unable to recognize empty encryption properties")
+ }
+ return &CBCSecureMaterials{
+ srcBuf: bytes.NewBuffer([]byte{}),
+ dstBuf: bytes.NewBuffer([]byte{}),
+ encryptionKey: key,
+ matDesc: []byte("{}"),
+ }, nil
+
+}
+
+// SetupEncryptMode - tells CBC that we are going to encrypt data
+func (s *CBCSecureMaterials) SetupEncryptMode(stream io.Reader) error {
+ // Set mode to encrypt
+ s.cryptMode = encryptMode
+
+ // Set underlying reader
+ s.stream = stream
+
+ s.eof = false
+ s.srcBuf.Reset()
+ s.dstBuf.Reset()
+
+ var err error
+
+ // Generate random content key
+ s.contentKey = make([]byte, aes.BlockSize*2)
+ if _, err := rand.Read(s.contentKey); err != nil {
+ return err
+ }
+ // Encrypt content key
+ s.cryptedKey, err = s.encryptionKey.Encrypt(s.contentKey)
+ if err != nil {
+ return err
+ }
+ // Generate random IV
+ s.iv = make([]byte, aes.BlockSize)
+ if _, err = rand.Read(s.iv); err != nil {
+ return err
+ }
+ // New cipher
+ encryptContentBlock, err := aes.NewCipher(s.contentKey)
+ if err != nil {
+ return err
+ }
+
+ s.blockMode = cipher.NewCBCEncrypter(encryptContentBlock, s.iv)
+
+ return nil
+}
+
+// SetupDecryptMode - tells CBC that we are going to decrypt data
+func (s *CBCSecureMaterials) SetupDecryptMode(stream io.Reader, iv string, key string) error {
+ // Set mode to decrypt
+ s.cryptMode = decryptMode
+
+ // Set underlying reader
+ s.stream = stream
+
+ // Reset
+ s.eof = false
+ s.srcBuf.Reset()
+ s.dstBuf.Reset()
+
+ var err error
+
+ // Get IV
+ s.iv, err = base64.StdEncoding.DecodeString(iv)
+ if err != nil {
+ return err
+ }
+
+ // Get encrypted content key
+ s.cryptedKey, err = base64.StdEncoding.DecodeString(key)
+ if err != nil {
+ return err
+ }
+
+ // Decrypt content key
+ s.contentKey, err = s.encryptionKey.Decrypt(s.cryptedKey)
+ if err != nil {
+ return err
+ }
+
+ // New cipher
+ decryptContentBlock, err := aes.NewCipher(s.contentKey)
+ if err != nil {
+ return err
+ }
+
+ s.blockMode = cipher.NewCBCDecrypter(decryptContentBlock, s.iv)
+ return nil
+}
+
+// GetIV - return randomly generated IV (per S3 object), base64 encoded.
+func (s *CBCSecureMaterials) GetIV() string {
+ return base64.StdEncoding.EncodeToString(s.iv)
+}
+
+// GetKey - return content encrypting key (cek) in encrypted form, base64 encoded.
+func (s *CBCSecureMaterials) GetKey() string {
+ return base64.StdEncoding.EncodeToString(s.cryptedKey)
+}
+
+// GetDesc - user provided encryption material description in JSON (UTF8) format.
+func (s *CBCSecureMaterials) GetDesc() string {
+ return string(s.matDesc)
+}
+
+// Fill buf with encrypted/decrypted data
+func (s *CBCSecureMaterials) Read(buf []byte) (n int, err error) {
+ // Always fill buf from bufChunk at the end of this function
+ defer func() {
+ if s.err != nil {
+ n, err = 0, s.err
+ } else {
+ n, err = s.dstBuf.Read(buf)
+ }
+ }()
+
+ // Return
+ if s.eof {
+ return
+ }
+
+ // Fill dest buffer if its length is less than buf
+ for !s.eof && s.dstBuf.Len() < len(buf) {
+
+ srcPart := make([]byte, aes.BlockSize)
+ dstPart := make([]byte, aes.BlockSize)
+
+ // Fill src buffer
+ for s.srcBuf.Len() < aes.BlockSize*2 {
+ _, err = io.CopyN(s.srcBuf, s.stream, aes.BlockSize)
+ if err != nil {
+ break
+ }
+ }
+
+ // Quit immediately for errors other than io.EOF
+ if err != nil && err != io.EOF {
+ s.err = err
+ return
+ }
+
+ // Mark current encrypting/decrypting as finished
+ s.eof = (err == io.EOF)
+
+ if s.eof && s.cryptMode == encryptMode {
+ if srcPart, err = pkcs5Pad(s.srcBuf.Bytes(), aes.BlockSize); err != nil {
+ s.err = err
+ return
+ }
+ } else {
+ _, _ = s.srcBuf.Read(srcPart)
+ }
+
+ // Crypt srcPart content
+ for len(srcPart) > 0 {
+
+ // Crypt current part
+ s.blockMode.CryptBlocks(dstPart, srcPart[:aes.BlockSize])
+
+ // Unpad when this is the last part and we are decrypting
+ if s.eof && s.cryptMode == decryptMode {
+ dstPart, err = pkcs5Unpad(dstPart, aes.BlockSize)
+ if err != nil {
+ s.err = err
+ return
+ }
+ }
+
+ // Send crypted data to dstBuf
+ if _, wErr := s.dstBuf.Write(dstPart); wErr != nil {
+ s.err = wErr
+ return
+ }
+ // Move to the next part
+ srcPart = srcPart[aes.BlockSize:]
+ }
+ }
+ return
+}
+
+// Unpad a set of bytes following PKCS5 algorithm
+func pkcs5Unpad(buf []byte, blockSize int) ([]byte, error) {
+ len := len(buf)
+ if len == 0 {
+ return nil, errors.New("buffer is empty")
+ }
+ pad := int(buf[len-1])
+ if pad > len || pad > blockSize {
+ return nil, errors.New("invalid padding size")
+ }
+ return buf[:len-pad], nil
+}
+
+// Pad a set of bytes following PKCS5 algorithm
+func pkcs5Pad(buf []byte, blockSize int) ([]byte, error) {
+ len := len(buf)
+ pad := blockSize - (len % blockSize)
+ padText := bytes.Repeat([]byte{byte(pad)}, pad)
+ return append(buf, padText...), nil
+}
diff --git a/vendor/github.com/minio/minio-go/pkg/encrypt/interface.go b/vendor/github.com/minio/minio-go/pkg/encrypt/interface.go
new file mode 100644
index 000000000..2fd75033f
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/pkg/encrypt/interface.go
@@ -0,0 +1,50 @@
+/*
+ * Minio Go Library for Amazon S3 Compatible Cloud Storage (C) 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 encrypt implements a generic interface to encrypt any stream of data.
+// currently this package implements two types of encryption
+// - Symmetric encryption using AES.
+// - Asymmetric encrytion using RSA.
+package encrypt
+
+import "io"
+
+// Materials - provides generic interface to encrypt any stream of data.
+type Materials interface {
+
+ // Returns encrypted/decrypted data, io.Reader compatible.
+ Read(b []byte) (int, error)
+
+ // Get randomly generated IV, base64 encoded.
+ GetIV() (iv string)
+
+ // Get content encrypting key (cek) in encrypted form, base64 encoded.
+ GetKey() (key string)
+
+ // Get user provided encryption material description in
+ // JSON (UTF8) format. This is not used, kept for future.
+ GetDesc() (desc string)
+
+ // Setup encrypt mode, further calls of Read() function
+ // will return the encrypted form of data streamed
+ // by the passed reader
+ SetupEncryptMode(stream io.Reader) error
+
+ // Setup decrypted mode, further calls of Read() function
+ // will return the decrypted form of data streamed
+ // by the passed reader
+ SetupDecryptMode(stream io.Reader, iv string, key string) error
+}
diff --git a/vendor/github.com/minio/minio-go/pkg/encrypt/keys.go b/vendor/github.com/minio/minio-go/pkg/encrypt/keys.go
new file mode 100644
index 000000000..8814845e3
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/pkg/encrypt/keys.go
@@ -0,0 +1,165 @@
+/*
+ * Minio Go Library for Amazon S3 Compatible Cloud Storage (C) 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 encrypt
+
+import (
+ "crypto/aes"
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/x509"
+ "errors"
+)
+
+// Key - generic interface to encrypt/decrypt a key.
+// We use it to encrypt/decrypt content key which is the key
+// that encrypt/decrypt object data.
+type Key interface {
+ // Encrypt data using to the set encryption key
+ Encrypt([]byte) ([]byte, error)
+ // Decrypt data using to the set encryption key
+ Decrypt([]byte) ([]byte, error)
+}
+
+// SymmetricKey - encrypts data with a symmetric master key
+type SymmetricKey struct {
+ masterKey []byte
+}
+
+// Encrypt passed bytes
+func (s *SymmetricKey) Encrypt(plain []byte) ([]byte, error) {
+ // Initialize an AES encryptor using a master key
+ keyBlock, err := aes.NewCipher(s.masterKey)
+ if err != nil {
+ return []byte{}, err
+ }
+
+ // Pad the key before encryption
+ plain, _ = pkcs5Pad(plain, aes.BlockSize)
+
+ encKey := []byte{}
+ encPart := make([]byte, aes.BlockSize)
+
+ // Encrypt the passed key by block
+ for {
+ if len(plain) < aes.BlockSize {
+ break
+ }
+ // Encrypt the passed key
+ keyBlock.Encrypt(encPart, plain[:aes.BlockSize])
+ // Add the encrypted block to the total encrypted key
+ encKey = append(encKey, encPart...)
+ // Pass to the next plain block
+ plain = plain[aes.BlockSize:]
+ }
+ return encKey, nil
+}
+
+// Decrypt passed bytes
+func (s *SymmetricKey) Decrypt(cipher []byte) ([]byte, error) {
+ // Initialize AES decrypter
+ keyBlock, err := aes.NewCipher(s.masterKey)
+ if err != nil {
+ return nil, err
+ }
+
+ var plain []byte
+ plainPart := make([]byte, aes.BlockSize)
+
+ // Decrypt the encrypted data block by block
+ for {
+ if len(cipher) < aes.BlockSize {
+ break
+ }
+ keyBlock.Decrypt(plainPart, cipher[:aes.BlockSize])
+ // Add the decrypted block to the total result
+ plain = append(plain, plainPart...)
+ // Pass to the next cipher block
+ cipher = cipher[aes.BlockSize:]
+ }
+
+ // Unpad the resulted plain data
+ plain, err = pkcs5Unpad(plain, aes.BlockSize)
+ if err != nil {
+ return nil, err
+ }
+
+ return plain, nil
+}
+
+// NewSymmetricKey generates a new encrypt/decrypt crypto using
+// an AES master key password
+func NewSymmetricKey(b []byte) *SymmetricKey {
+ return &SymmetricKey{masterKey: b}
+}
+
+// AsymmetricKey - struct which encrypts/decrypts data
+// using RSA public/private certificates
+type AsymmetricKey struct {
+ publicKey *rsa.PublicKey
+ privateKey *rsa.PrivateKey
+}
+
+// Encrypt data using public key
+func (a *AsymmetricKey) Encrypt(plain []byte) ([]byte, error) {
+ cipher, err := rsa.EncryptPKCS1v15(rand.Reader, a.publicKey, plain)
+ if err != nil {
+ return nil, err
+ }
+ return cipher, nil
+}
+
+// Decrypt data using public key
+func (a *AsymmetricKey) Decrypt(cipher []byte) ([]byte, error) {
+ cipher, err := rsa.DecryptPKCS1v15(rand.Reader, a.privateKey, cipher)
+ if err != nil {
+ return nil, err
+ }
+ return cipher, nil
+}
+
+// NewAsymmetricKey - generates a crypto module able to encrypt/decrypt
+// data using a pair for private and public key
+func NewAsymmetricKey(privData []byte, pubData []byte) (*AsymmetricKey, error) {
+ // Parse private key from passed data
+ priv, err := x509.ParsePKCS8PrivateKey(privData)
+ if err != nil {
+ return nil, err
+ }
+ privKey, ok := priv.(*rsa.PrivateKey)
+ if !ok {
+ return nil, errors.New("not a valid private key")
+ }
+
+ // Parse public key from passed data
+ pub, err := x509.ParsePKIXPublicKey(pubData)
+ if err != nil {
+ return nil, err
+ }
+
+ pubKey, ok := pub.(*rsa.PublicKey)
+ if !ok {
+ return nil, errors.New("not a valid public key")
+ }
+
+ // Associate the private key with the passed public key
+ privKey.PublicKey = *pubKey
+
+ return &AsymmetricKey{
+ publicKey: pubKey,
+ privateKey: privKey,
+ }, nil
+}
diff --git a/vendor/github.com/minio/minio-go/pkg/policy/bucket-policy.go b/vendor/github.com/minio/minio-go/pkg/policy/bucket-policy.go
index cbb889d8d..b2d46e178 100644
--- a/vendor/github.com/minio/minio-go/pkg/policy/bucket-policy.go
+++ b/vendor/github.com/minio/minio-go/pkg/policy/bucket-policy.go
@@ -583,7 +583,7 @@ func GetPolicies(statements []Statement, bucketName string) map[string]BucketPol
r = r[:len(r)-1]
asterisk = "*"
}
- objectPath := r[len(awsResourcePrefix+bucketName)+1 : len(r)]
+ objectPath := r[len(awsResourcePrefix+bucketName)+1:]
p := GetPolicy(statements, bucketName, objectPath)
policyRules[bucketName+"/"+objectPath+asterisk] = p
}
diff --git a/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-streaming.go b/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-streaming.go
new file mode 100644
index 000000000..c2f0baee6
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-streaming.go
@@ -0,0 +1,290 @@
+/*
+ * Minio Go Library for Amazon S3 Compatible Cloud Storage (C) 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 (
+ "bytes"
+ "encoding/hex"
+ "fmt"
+ "io"
+ "net/http"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// Reference for constants used below -
+// http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html#example-signature-calculations-streaming
+const (
+ streamingSignAlgorithm = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD"
+ streamingEncoding = "aws-chunked"
+ streamingPayloadHdr = "AWS4-HMAC-SHA256-PAYLOAD"
+ emptySHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
+ payloadChunkSize = 64 * 1024
+ chunkSigConstLen = 17 // ";chunk-signature="
+ signatureStrLen = 64 // e.g. "f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2"
+ crlfLen = 2 // CRLF
+)
+
+// Request headers to be ignored while calculating seed signature for
+// a request.
+var ignoredStreamingHeaders = map[string]bool{
+ "Authorization": true,
+ "User-Agent": true,
+ "Content-Type": true,
+}
+
+// getSignedChunkLength - calculates the length of chunk metadata
+func getSignedChunkLength(chunkDataSize int64) int64 {
+ return int64(len(fmt.Sprintf("%x", chunkDataSize))) +
+ chunkSigConstLen +
+ signatureStrLen +
+ crlfLen +
+ chunkDataSize +
+ crlfLen
+}
+
+// getStreamLength - calculates the length of the overall stream (data + metadata)
+func getStreamLength(dataLen, chunkSize int64) int64 {
+ if dataLen <= 0 {
+ return 0
+ }
+
+ chunksCount := int64(dataLen / chunkSize)
+ remainingBytes := int64(dataLen % chunkSize)
+ streamLen := int64(0)
+ streamLen += chunksCount * getSignedChunkLength(chunkSize)
+ if remainingBytes > 0 {
+ streamLen += getSignedChunkLength(remainingBytes)
+ }
+ streamLen += getSignedChunkLength(0)
+ return streamLen
+}
+
+// buildChunkStringToSign - returns the string to sign given chunk data
+// and previous signature.
+func buildChunkStringToSign(t time.Time, region, previousSig string, chunkData []byte) string {
+ stringToSignParts := []string{
+ streamingPayloadHdr,
+ t.Format(iso8601DateFormat),
+ getScope(region, t),
+ previousSig,
+ emptySHA256,
+ hex.EncodeToString(sum256(chunkData)),
+ }
+
+ return strings.Join(stringToSignParts, "\n")
+}
+
+// prepareStreamingRequest - prepares a request with appropriate
+// headers before computing the seed signature.
+func prepareStreamingRequest(req *http.Request, sessionToken string, dataLen int64, timestamp time.Time) {
+ // Set x-amz-content-sha256 header.
+ req.Header.Set("X-Amz-Content-Sha256", streamingSignAlgorithm)
+ if sessionToken != "" {
+ req.Header.Set("X-Amz-Security-Token", sessionToken)
+ }
+ req.Header.Set("Content-Encoding", streamingEncoding)
+ req.Header.Set("X-Amz-Date", timestamp.Format(iso8601DateFormat))
+
+ // Set content length with streaming signature for each chunk included.
+ req.ContentLength = getStreamLength(dataLen, int64(payloadChunkSize))
+ req.Header.Set("x-amz-decoded-content-length", strconv.FormatInt(dataLen, 10))
+}
+
+// buildChunkHeader - returns the chunk header.
+// e.g string(IntHexBase(chunk-size)) + ";chunk-signature=" + signature + \r\n + chunk-data + \r\n
+func buildChunkHeader(chunkLen int64, signature string) []byte {
+ return []byte(strconv.FormatInt(chunkLen, 16) + ";chunk-signature=" + signature + "\r\n")
+}
+
+// buildChunkSignature - returns chunk signature for a given chunk and previous signature.
+func buildChunkSignature(chunkData []byte, reqTime time.Time, region,
+ previousSignature, secretAccessKey string) string {
+
+ chunkStringToSign := buildChunkStringToSign(reqTime, region,
+ previousSignature, chunkData)
+ signingKey := getSigningKey(secretAccessKey, region, reqTime)
+ return getSignature(signingKey, chunkStringToSign)
+}
+
+// getSeedSignature - returns the seed signature for a given request.
+func (s *StreamingReader) setSeedSignature(req *http.Request) {
+ // Get canonical request
+ canonicalRequest := getCanonicalRequest(*req, ignoredStreamingHeaders)
+
+ // Get string to sign from canonical request.
+ stringToSign := getStringToSignV4(s.reqTime, s.region, canonicalRequest)
+
+ signingKey := getSigningKey(s.secretAccessKey, s.region, s.reqTime)
+
+ // Calculate signature.
+ s.seedSignature = getSignature(signingKey, stringToSign)
+}
+
+// StreamingReader implements chunked upload signature as a reader on
+// top of req.Body's ReaderCloser chunk header;data;... repeat
+type StreamingReader struct {
+ accessKeyID string
+ secretAccessKey string
+ sessionToken string
+ region string
+ prevSignature string
+ seedSignature string
+ contentLen int64 // Content-Length from req header
+ baseReadCloser io.ReadCloser // underlying io.Reader
+ bytesRead int64 // bytes read from underlying io.Reader
+ buf bytes.Buffer // holds signed chunk
+ chunkBuf []byte // holds raw data read from req Body
+ chunkBufLen int // no. of bytes read so far into chunkBuf
+ done bool // done reading the underlying reader to EOF
+ reqTime time.Time
+ chunkNum int
+ totalChunks int
+ lastChunkSize int
+}
+
+// signChunk - signs a chunk read from s.baseReader of chunkLen size.
+func (s *StreamingReader) signChunk(chunkLen int) {
+ // Compute chunk signature for next header
+ signature := buildChunkSignature(s.chunkBuf[:chunkLen], s.reqTime,
+ s.region, s.prevSignature, s.secretAccessKey)
+
+ // For next chunk signature computation
+ s.prevSignature = signature
+
+ // Write chunk header into streaming buffer
+ chunkHdr := buildChunkHeader(int64(chunkLen), signature)
+ s.buf.Write(chunkHdr)
+
+ // Write chunk data into streaming buffer
+ s.buf.Write(s.chunkBuf[:chunkLen])
+
+ // Write the chunk trailer.
+ s.buf.Write([]byte("\r\n"))
+
+ // Reset chunkBufLen for next chunk read.
+ s.chunkBufLen = 0
+ s.chunkNum++
+}
+
+// setStreamingAuthHeader - builds and sets authorization header value
+// for streaming signature.
+func (s *StreamingReader) setStreamingAuthHeader(req *http.Request) {
+ credential := GetCredential(s.accessKeyID, s.region, s.reqTime)
+ authParts := []string{
+ signV4Algorithm + " Credential=" + credential,
+ "SignedHeaders=" + getSignedHeaders(*req, ignoredStreamingHeaders),
+ "Signature=" + s.seedSignature,
+ }
+
+ // Set authorization header.
+ auth := strings.Join(authParts, ",")
+ req.Header.Set("Authorization", auth)
+}
+
+// StreamingSignV4 - provides chunked upload signatureV4 support by
+// implementing io.Reader.
+func StreamingSignV4(req *http.Request, accessKeyID, secretAccessKey, sessionToken,
+ region string, dataLen int64, reqTime time.Time) *http.Request {
+
+ // Set headers needed for streaming signature.
+ prepareStreamingRequest(req, sessionToken, dataLen, reqTime)
+
+ stReader := &StreamingReader{
+ baseReadCloser: req.Body,
+ accessKeyID: accessKeyID,
+ secretAccessKey: secretAccessKey,
+ sessionToken: sessionToken,
+ region: region,
+ reqTime: reqTime,
+ chunkBuf: make([]byte, payloadChunkSize),
+ contentLen: dataLen,
+ chunkNum: 1,
+ totalChunks: int((dataLen+payloadChunkSize-1)/payloadChunkSize) + 1,
+ lastChunkSize: int(dataLen % payloadChunkSize),
+ }
+
+ // Add the request headers required for chunk upload signing.
+
+ // Compute the seed signature.
+ stReader.setSeedSignature(req)
+
+ // Set the authorization header with the seed signature.
+ stReader.setStreamingAuthHeader(req)
+
+ // Set seed signature as prevSignature for subsequent
+ // streaming signing process.
+ stReader.prevSignature = stReader.seedSignature
+ req.Body = stReader
+
+ return req
+}
+
+// Read - this method performs chunk upload signature providing a
+// io.Reader interface.
+func (s *StreamingReader) Read(buf []byte) (int, error) {
+ switch {
+ // After the last chunk is read from underlying reader, we
+ // never re-fill s.buf.
+ case s.done:
+
+ // s.buf will be (re-)filled with next chunk when has lesser
+ // bytes than asked for.
+ case s.buf.Len() < len(buf):
+ s.chunkBufLen = 0
+ for {
+ n1, err := s.baseReadCloser.Read(s.chunkBuf[s.chunkBufLen:])
+ if err == nil || err == io.ErrUnexpectedEOF {
+ s.chunkBufLen += n1
+ s.bytesRead += int64(n1)
+
+ if s.chunkBufLen == payloadChunkSize ||
+ (s.chunkNum == s.totalChunks-1 &&
+ s.chunkBufLen == s.lastChunkSize) {
+ // Sign the chunk and write it to s.buf.
+ s.signChunk(s.chunkBufLen)
+ break
+ }
+
+ } else if err == io.EOF {
+ // No more data left in baseReader - last chunk.
+ // Done reading the last chunk from baseReader.
+ s.done = true
+
+ // bytes read from baseReader different than
+ // content length provided.
+ if s.bytesRead != s.contentLen {
+ return 0, io.ErrUnexpectedEOF
+ }
+
+ // Sign the chunk and write it to s.buf.
+ s.signChunk(0)
+ break
+
+ } else {
+ return 0, err
+ }
+ }
+ }
+ return s.buf.Read(buf)
+}
+
+// Close - this method makes underlying io.ReadCloser's Close method available.
+func (s *StreamingReader) Close() error {
+ return s.baseReadCloser.Close()
+}
diff --git a/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-streaming_test.go b/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-streaming_test.go
new file mode 100644
index 000000000..1f49f2234
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-streaming_test.go
@@ -0,0 +1,106 @@
+/*
+ * Minio Go Library for Amazon S3 Compatible Cloud Storage (C) 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 (
+ "bytes"
+ "io/ioutil"
+ "testing"
+ "time"
+)
+
+func TestGetSeedSignature(t *testing.T) {
+ accessKeyID := "AKIAIOSFODNN7EXAMPLE"
+ secretAccessKeyID := "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
+ dataLen := 66560
+ data := bytes.Repeat([]byte("a"), dataLen)
+ body := ioutil.NopCloser(bytes.NewReader(data))
+
+ req := NewRequest("PUT", "/examplebucket/chunkObject.txt", body)
+ req.Header.Set("x-amz-storage-class", "REDUCED_REDUNDANCY")
+ req.URL.Host = "s3.amazonaws.com"
+
+ reqTime, err := time.Parse("20060102T150405Z", "20130524T000000Z")
+ if err != nil {
+ t.Fatalf("Failed to parse time - %v", err)
+ }
+
+ req = StreamingSignV4(req, accessKeyID, secretAccessKeyID, "", "us-east-1", int64(dataLen), reqTime)
+ actualSeedSignature := req.Body.(*StreamingReader).seedSignature
+
+ expectedSeedSignature := "007480502de61457e955731b0f5d191f7e6f54a8a0f6cc7974a5ebd887965686"
+ if actualSeedSignature != expectedSeedSignature {
+ t.Errorf("Expected %s but received %s", expectedSeedSignature, actualSeedSignature)
+ }
+}
+
+func TestChunkSignature(t *testing.T) {
+ chunkData := bytes.Repeat([]byte("a"), 65536)
+ reqTime, _ := time.Parse(iso8601DateFormat, "20130524T000000Z")
+ previousSignature := "4f232c4386841ef735655705268965c44a0e4690baa4adea153f7db9fa80a0a9"
+ location := "us-east-1"
+ secretAccessKeyID := "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
+ expectedSignature := "ad80c730a21e5b8d04586a2213dd63b9a0e99e0e2307b0ade35a65485a288648"
+ actualSignature := buildChunkSignature(chunkData, reqTime, location, previousSignature, secretAccessKeyID)
+ if actualSignature != expectedSignature {
+ t.Errorf("Expected %s but received %s", expectedSignature, actualSignature)
+ }
+}
+
+func TestSetStreamingAuthorization(t *testing.T) {
+ location := "us-east-1"
+ secretAccessKeyID := "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
+ accessKeyID := "AKIAIOSFODNN7EXAMPLE"
+
+ req := NewRequest("PUT", "/examplebucket/chunkObject.txt", nil)
+ req.Header.Set("x-amz-storage-class", "REDUCED_REDUNDANCY")
+ req.URL.Host = "s3.amazonaws.com"
+
+ dataLen := int64(65 * 1024)
+ reqTime, _ := time.Parse(iso8601DateFormat, "20130524T000000Z")
+ req = StreamingSignV4(req, accessKeyID, secretAccessKeyID, "", location, dataLen, reqTime)
+
+ expectedAuthorization := "AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request,SignedHeaders=content-encoding;host;x-amz-content-sha256;x-amz-date;x-amz-decoded-content-length;x-amz-storage-class,Signature=007480502de61457e955731b0f5d191f7e6f54a8a0f6cc7974a5ebd887965686"
+
+ actualAuthorization := req.Header.Get("Authorization")
+ if actualAuthorization != expectedAuthorization {
+ t.Errorf("Expected %s but received %s", expectedAuthorization, actualAuthorization)
+ }
+}
+
+func TestStreamingReader(t *testing.T) {
+ reqTime, _ := time.Parse("20060102T150405Z", "20130524T000000Z")
+ location := "us-east-1"
+ secretAccessKeyID := "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
+ accessKeyID := "AKIAIOSFODNN7EXAMPLE"
+ dataLen := int64(65 * 1024)
+
+ req := NewRequest("PUT", "/examplebucket/chunkObject.txt", nil)
+ req.Header.Set("x-amz-storage-class", "REDUCED_REDUNDANCY")
+ req.ContentLength = 65 * 1024
+ req.URL.Host = "s3.amazonaws.com"
+
+ baseReader := ioutil.NopCloser(bytes.NewReader(bytes.Repeat([]byte("a"), 65*1024)))
+ req.Body = baseReader
+ req = StreamingSignV4(req, accessKeyID, secretAccessKeyID, "", location, dataLen, reqTime)
+
+ b, err := ioutil.ReadAll(req.Body)
+ if err != nil {
+ t.Errorf("Expected no error but received %v %d", err, len(b))
+ }
+ req.Body.Close()
+}
diff --git a/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-v2.go b/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-v2.go
index e1ec6c02c..af0e91515 100644
--- a/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-v2.go
+++ b/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-v2.go
@@ -316,7 +316,7 @@ func writeCanonicalizedResource(buf *bytes.Buffer, req http.Request, isPreSign b
// Request parameters
if len(vv[0]) > 0 {
buf.WriteByte('=')
- buf.WriteString(strings.Replace(url.QueryEscape(vv[0]), "+", "%20", -1))
+ buf.WriteString(vv[0])
}
}
}
diff --git a/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-v4.go b/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-v4.go
index 3322b67cc..0d75dc162 100644
--- a/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-v4.go
+++ b/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-v4.go
@@ -70,7 +70,7 @@ const (
///
/// Is skipped for obvious reasons
///
-var ignoredHeaders = map[string]bool{
+var v4IgnoredHeaders = map[string]bool{
"Authorization": true,
"Content-Type": true,
"Content-Length": true,
@@ -122,7 +122,7 @@ func getHashedPayload(req http.Request) string {
// getCanonicalHeaders generate a list of request headers for
// signature.
-func getCanonicalHeaders(req http.Request) string {
+func getCanonicalHeaders(req http.Request, ignoredHeaders map[string]bool) string {
var headers []string
vals := make(map[string][]string)
for k, vv := range req.Header {
@@ -161,7 +161,7 @@ func getCanonicalHeaders(req http.Request) string {
// getSignedHeaders generate all signed request headers.
// i.e lexically sorted, semicolon-separated list of lowercase
// request header names.
-func getSignedHeaders(req http.Request) string {
+func getSignedHeaders(req http.Request, ignoredHeaders map[string]bool) string {
var headers []string
for k := range req.Header {
if _, ok := ignoredHeaders[http.CanonicalHeaderKey(k)]; ok {
@@ -183,14 +183,14 @@ func getSignedHeaders(req http.Request) string {
// <CanonicalHeaders>\n
// <SignedHeaders>\n
// <HashedPayload>
-func getCanonicalRequest(req http.Request) string {
+func getCanonicalRequest(req http.Request, ignoredHeaders map[string]bool) string {
req.URL.RawQuery = strings.Replace(req.URL.Query().Encode(), "+", "%20", -1)
canonicalRequest := strings.Join([]string{
req.Method,
s3utils.EncodePath(req.URL.Path),
req.URL.RawQuery,
- getCanonicalHeaders(req),
- getSignedHeaders(req),
+ getCanonicalHeaders(req, ignoredHeaders),
+ getSignedHeaders(req, ignoredHeaders),
getHashedPayload(req),
}, "\n")
return canonicalRequest
@@ -206,7 +206,7 @@ func getStringToSignV4(t time.Time, location, canonicalRequest string) string {
// PreSignV4 presign the request, in accordance with
// http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html.
-func PreSignV4(req http.Request, accessKeyID, secretAccessKey, location string, expires int64) *http.Request {
+func PreSignV4(req http.Request, accessKeyID, secretAccessKey, sessionToken, location string, expires int64) *http.Request {
// Presign is not needed for anonymous credentials.
if accessKeyID == "" || secretAccessKey == "" {
return &req
@@ -219,7 +219,7 @@ func PreSignV4(req http.Request, accessKeyID, secretAccessKey, location string,
credential := GetCredential(accessKeyID, location, t)
// Get all signed headers.
- signedHeaders := getSignedHeaders(req)
+ signedHeaders := getSignedHeaders(req, v4IgnoredHeaders)
// Set URL query.
query := req.URL.Query()
@@ -228,10 +228,14 @@ func PreSignV4(req http.Request, accessKeyID, secretAccessKey, location string,
query.Set("X-Amz-Expires", strconv.FormatInt(expires, 10))
query.Set("X-Amz-SignedHeaders", signedHeaders)
query.Set("X-Amz-Credential", credential)
+ // Set session token if available.
+ if sessionToken != "" {
+ query.Set("X-Amz-Security-Token", sessionToken)
+ }
req.URL.RawQuery = query.Encode()
// Get canonical request.
- canonicalRequest := getCanonicalRequest(req)
+ canonicalRequest := getCanonicalRequest(req, v4IgnoredHeaders)
// Get string to sign from canonical request.
stringToSign := getStringToSignV4(t, location, canonicalRequest)
@@ -260,7 +264,7 @@ func PostPresignSignatureV4(policyBase64 string, t time.Time, secretAccessKey, l
// SignV4 sign the request before Do(), in accordance with
// http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html.
-func SignV4(req http.Request, accessKeyID, secretAccessKey, location string) *http.Request {
+func SignV4(req http.Request, accessKeyID, secretAccessKey, sessionToken, location string) *http.Request {
// Signature calculation is not needed for anonymous credentials.
if accessKeyID == "" || secretAccessKey == "" {
return &req
@@ -272,8 +276,13 @@ func SignV4(req http.Request, accessKeyID, secretAccessKey, location string) *ht
// Set x-amz-date.
req.Header.Set("X-Amz-Date", t.Format(iso8601DateFormat))
+ // Set session token if available.
+ if sessionToken != "" {
+ req.Header.Set("X-Amz-Security-Token", sessionToken)
+ }
+
// Get canonical request.
- canonicalRequest := getCanonicalRequest(req)
+ canonicalRequest := getCanonicalRequest(req, v4IgnoredHeaders)
// Get string to sign from canonical request.
stringToSign := getStringToSignV4(t, location, canonicalRequest)
@@ -285,7 +294,7 @@ func SignV4(req http.Request, accessKeyID, secretAccessKey, location string) *ht
credential := GetCredential(accessKeyID, location, t)
// Get all signed headers.
- signedHeaders := getSignedHeaders(req)
+ signedHeaders := getSignedHeaders(req, v4IgnoredHeaders)
// Calculate signature.
signature := getSignature(signingKey, stringToSign)
diff --git a/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature_test.go b/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature_test.go
index 6f5ba1895..85ff063df 100644
--- a/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature_test.go
+++ b/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature_test.go
@@ -28,12 +28,12 @@ func TestSignatureCalculation(t *testing.T) {
if err != nil {
t.Fatal("Error:", err)
}
- req = SignV4(*req, "", "", "us-east-1")
+ req = SignV4(*req, "", "", "", "us-east-1")
if req.Header.Get("Authorization") != "" {
t.Fatal("Error: anonymous credentials should not have Authorization header.")
}
- req = PreSignV4(*req, "", "", "us-east-1", 0)
+ req = PreSignV4(*req, "", "", "", "us-east-1", 0)
if strings.Contains(req.URL.RawQuery, "X-Amz-Signature") {
t.Fatal("Error: anonymous credentials should not have Signature query resource.")
}
@@ -48,12 +48,12 @@ func TestSignatureCalculation(t *testing.T) {
t.Fatal("Error: anonymous credentials should not have Signature query resource.")
}
- req = SignV4(*req, "ACCESS-KEY", "SECRET-KEY", "us-east-1")
+ req = SignV4(*req, "ACCESS-KEY", "SECRET-KEY", "", "us-east-1")
if req.Header.Get("Authorization") == "" {
t.Fatal("Error: normal credentials should have Authorization header.")
}
- req = PreSignV4(*req, "ACCESS-KEY", "SECRET-KEY", "us-east-1", 0)
+ req = PreSignV4(*req, "ACCESS-KEY", "SECRET-KEY", "", "us-east-1", 0)
if !strings.Contains(req.URL.RawQuery, "X-Amz-Signature") {
t.Fatal("Error: normal credentials should have Signature query resource.")
}
diff --git a/vendor/github.com/minio/minio-go/pkg/s3signer/test-utils_test.go b/vendor/github.com/minio/minio-go/pkg/s3signer/test-utils_test.go
new file mode 100644
index 000000000..049e5813d
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/pkg/s3signer/test-utils_test.go
@@ -0,0 +1,103 @@
+/*
+ * Minio Go Library for Amazon S3 Compatible Cloud Storage (C) 2015 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 (
+ "bufio"
+ "bytes"
+ "crypto/tls"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "strings"
+)
+
+// N B minio-go should compile on go1.5.3 onwards and httptest package is
+// available only from go.1.7.x. The following function is taken from
+// Go httptest package to be able to build on older versions of Go.
+
+// NewRequest returns a new incoming server Request, suitable
+// for passing to an http.Handler for testing.
+//
+// The target is the RFC 7230 "request-target": it may be either a
+// path or an absolute URL. If target is an absolute URL, the host name
+// from the URL is used. Otherwise, "example.com" is used.
+//
+// The TLS field is set to a non-nil dummy value if target has scheme
+// "https".
+//
+// The Request.Proto is always HTTP/1.1.
+//
+// An empty method means "GET".
+//
+// The provided body may be nil. If the body is of type *bytes.Reader,
+// *strings.Reader, or *bytes.Buffer, the Request.ContentLength is
+// set.
+//
+// NewRequest panics on error for ease of use in testing, where a
+// panic is acceptable.
+func NewRequest(method, target string, body io.Reader) *http.Request {
+ if method == "" {
+ method = "GET"
+ }
+ req, err := http.ReadRequest(bufio.NewReader(strings.NewReader(method + " " + target + " HTTP/1.0\r\n\r\n")))
+ if err != nil {
+ panic("invalid NewRequest arguments; " + err.Error())
+ }
+
+ // HTTP/1.0 was used above to avoid needing a Host field. Change it to 1.1 here.
+ req.Proto = "HTTP/1.1"
+ req.ProtoMinor = 1
+ req.Close = false
+
+ if body != nil {
+ switch v := body.(type) {
+ case *bytes.Buffer:
+ req.ContentLength = int64(v.Len())
+ case *bytes.Reader:
+ req.ContentLength = int64(v.Len())
+ case *strings.Reader:
+ req.ContentLength = int64(v.Len())
+ default:
+ req.ContentLength = -1
+ }
+ if rc, ok := body.(io.ReadCloser); ok {
+ req.Body = rc
+ } else {
+ req.Body = ioutil.NopCloser(body)
+ }
+ }
+
+ // 192.0.2.0/24 is "TEST-NET" in RFC 5737 for use solely in
+ // documentation and example source code and should not be
+ // used publicly.
+ req.RemoteAddr = "192.0.2.1:1234"
+
+ if req.Host == "" {
+ req.Host = "example.com"
+ }
+
+ if strings.HasPrefix(target, "https://") {
+ req.TLS = &tls.ConnectionState{
+ Version: tls.VersionTLS12,
+ HandshakeComplete: true,
+ ServerName: req.Host,
+ }
+ }
+
+ return req
+}