/* * MinIO Go Library for Amazon S3 Compatible Cloud Storage * Copyright 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" ) const ( // STSVersion sts version string STSVersion = "2011-06-15" // How much duration to slash from the given expiration duration defaultExpiryWindow = 0.8 ) // A Value is the S3 credentials value for individual credential fields. type Value struct { // S3 Access key ID AccessKeyID string // S3 Secret Access Key SecretAccessKey string // S3 Session Token SessionToken string // Expiration of this credentials - null means no expiration associated Expiration time.Time // 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) { if e.CurrentTime == nil { e.CurrentTime = time.Now } cut := window if cut < 0 { expireIn := expiration.Sub(e.CurrentTime()) cut = time.Duration(float64(expireIn) * (1 - defaultExpiryWindow)) } e.expiration = expiration.Add(-cut) } // 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) { if c == nil { return Value{}, nil } 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() }