1/*
2 * MinIO Go Library for Amazon S3 Compatible Cloud Storage
3 * Copyright 2017 MinIO, Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package credentials
19
20import (
21	"sync"
22	"time"
23)
24
25// STSVersion sts version string
26const STSVersion = "2011-06-15"
27
28// A Value is the AWS credentials value for individual credential fields.
29type Value struct {
30	// AWS Access key ID
31	AccessKeyID string
32
33	// AWS Secret Access Key
34	SecretAccessKey string
35
36	// AWS Session Token
37	SessionToken string
38
39	// Signature Type.
40	SignerType SignatureType
41}
42
43// A Provider is the interface for any component which will provide credentials
44// Value. A provider is required to manage its own Expired state, and what to
45// be expired means.
46type Provider interface {
47	// Retrieve returns nil if it successfully retrieved the value.
48	// Error is returned if the value were not obtainable, or empty.
49	Retrieve() (Value, error)
50
51	// IsExpired returns if the credentials are no longer valid, and need
52	// to be retrieved.
53	IsExpired() bool
54}
55
56// A Expiry provides shared expiration logic to be used by credentials
57// providers to implement expiry functionality.
58//
59// The best method to use this struct is as an anonymous field within the
60// provider's struct.
61//
62// Example:
63//     type IAMCredentialProvider struct {
64//         Expiry
65//         ...
66//     }
67type Expiry struct {
68	// The date/time when to expire on
69	expiration time.Time
70
71	// If set will be used by IsExpired to determine the current time.
72	// Defaults to time.Now if CurrentTime is not set.
73	CurrentTime func() time.Time
74}
75
76// SetExpiration sets the expiration IsExpired will check when called.
77//
78// If window is greater than 0 the expiration time will be reduced by the
79// window value.
80//
81// Using a window is helpful to trigger credentials to expire sooner than
82// the expiration time given to ensure no requests are made with expired
83// tokens.
84func (e *Expiry) SetExpiration(expiration time.Time, window time.Duration) {
85	e.expiration = expiration
86	if window > 0 {
87		e.expiration = e.expiration.Add(-window)
88	}
89}
90
91// IsExpired returns if the credentials are expired.
92func (e *Expiry) IsExpired() bool {
93	if e.CurrentTime == nil {
94		e.CurrentTime = time.Now
95	}
96	return e.expiration.Before(e.CurrentTime())
97}
98
99// Credentials - A container for synchronous safe retrieval of credentials Value.
100// Credentials will cache the credentials value until they expire. Once the value
101// expires the next Get will attempt to retrieve valid credentials.
102//
103// Credentials is safe to use across multiple goroutines and will manage the
104// synchronous state so the Providers do not need to implement their own
105// synchronization.
106//
107// The first Credentials.Get() will always call Provider.Retrieve() to get the
108// first instance of the credentials Value. All calls to Get() after that
109// will return the cached credentials Value until IsExpired() returns true.
110type Credentials struct {
111	sync.Mutex
112
113	creds        Value
114	forceRefresh bool
115	provider     Provider
116}
117
118// New returns a pointer to a new Credentials with the provider set.
119func New(provider Provider) *Credentials {
120	return &Credentials{
121		provider:     provider,
122		forceRefresh: true,
123	}
124}
125
126// Get returns the credentials value, or error if the credentials Value failed
127// to be retrieved.
128//
129// Will return the cached credentials Value if it has not expired. If the
130// credentials Value has expired the Provider's Retrieve() will be called
131// to refresh the credentials.
132//
133// If Credentials.Expire() was called the credentials Value will be force
134// expired, and the next call to Get() will cause them to be refreshed.
135func (c *Credentials) Get() (Value, error) {
136	if c == nil {
137		return Value{}, nil
138	}
139
140	c.Lock()
141	defer c.Unlock()
142
143	if c.isExpired() {
144		creds, err := c.provider.Retrieve()
145		if err != nil {
146			return Value{}, err
147		}
148		c.creds = creds
149		c.forceRefresh = false
150	}
151
152	return c.creds, nil
153}
154
155// Expire expires the credentials and forces them to be retrieved on the
156// next call to Get().
157//
158// This will override the Provider's expired state, and force Credentials
159// to call the Provider's Retrieve().
160func (c *Credentials) Expire() {
161	c.Lock()
162	defer c.Unlock()
163
164	c.forceRefresh = true
165}
166
167// IsExpired returns if the credentials are no longer valid, and need
168// to be refreshed.
169//
170// If the Credentials were forced to be expired with Expire() this will
171// reflect that override.
172func (c *Credentials) IsExpired() bool {
173	c.Lock()
174	defer c.Unlock()
175
176	return c.isExpired()
177}
178
179// isExpired helper method wrapping the definition of expired credentials.
180func (c *Credentials) isExpired() bool {
181	return c.forceRefresh || c.provider.IsExpired()
182}
183