credentials.go (5407B)
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 18 package credentials 19 20 import ( 21 "sync" 22 "time" 23 ) 24 25 const ( 26 // STSVersion sts version string 27 STSVersion = "2011-06-15" 28 29 // How much duration to slash from the given expiration duration 30 defaultExpiryWindow = 0.8 31 ) 32 33 // A Value is the AWS credentials value for individual credential fields. 34 type Value struct { 35 // AWS Access key ID 36 AccessKeyID string 37 38 // AWS Secret Access Key 39 SecretAccessKey string 40 41 // AWS Session Token 42 SessionToken string 43 44 // Signature Type. 45 SignerType SignatureType 46 } 47 48 // A Provider is the interface for any component which will provide credentials 49 // Value. A provider is required to manage its own Expired state, and what to 50 // be expired means. 51 type Provider interface { 52 // Retrieve returns nil if it successfully retrieved the value. 53 // Error is returned if the value were not obtainable, or empty. 54 Retrieve() (Value, error) 55 56 // IsExpired returns if the credentials are no longer valid, and need 57 // to be retrieved. 58 IsExpired() bool 59 } 60 61 // A Expiry provides shared expiration logic to be used by credentials 62 // providers to implement expiry functionality. 63 // 64 // The best method to use this struct is as an anonymous field within the 65 // provider's struct. 66 // 67 // Example: 68 // 69 // type IAMCredentialProvider struct { 70 // Expiry 71 // ... 72 // } 73 type Expiry struct { 74 // The date/time when to expire on 75 expiration time.Time 76 77 // If set will be used by IsExpired to determine the current time. 78 // Defaults to time.Now if CurrentTime is not set. 79 CurrentTime func() time.Time 80 } 81 82 // SetExpiration sets the expiration IsExpired will check when called. 83 // 84 // If window is greater than 0 the expiration time will be reduced by the 85 // window value. 86 // 87 // Using a window is helpful to trigger credentials to expire sooner than 88 // the expiration time given to ensure no requests are made with expired 89 // tokens. 90 func (e *Expiry) SetExpiration(expiration time.Time, window time.Duration) { 91 if e.CurrentTime == nil { 92 e.CurrentTime = time.Now 93 } 94 cut := window 95 if cut < 0 { 96 expireIn := expiration.Sub(e.CurrentTime()) 97 cut = time.Duration(float64(expireIn) * (1 - defaultExpiryWindow)) 98 } 99 e.expiration = expiration.Add(-cut) 100 } 101 102 // IsExpired returns if the credentials are expired. 103 func (e *Expiry) IsExpired() bool { 104 if e.CurrentTime == nil { 105 e.CurrentTime = time.Now 106 } 107 return e.expiration.Before(e.CurrentTime()) 108 } 109 110 // Credentials - A container for synchronous safe retrieval of credentials Value. 111 // Credentials will cache the credentials value until they expire. Once the value 112 // expires the next Get will attempt to retrieve valid credentials. 113 // 114 // Credentials is safe to use across multiple goroutines and will manage the 115 // synchronous state so the Providers do not need to implement their own 116 // synchronization. 117 // 118 // The first Credentials.Get() will always call Provider.Retrieve() to get the 119 // first instance of the credentials Value. All calls to Get() after that 120 // will return the cached credentials Value until IsExpired() returns true. 121 type Credentials struct { 122 sync.Mutex 123 124 creds Value 125 forceRefresh bool 126 provider Provider 127 } 128 129 // New returns a pointer to a new Credentials with the provider set. 130 func New(provider Provider) *Credentials { 131 return &Credentials{ 132 provider: provider, 133 forceRefresh: true, 134 } 135 } 136 137 // Get returns the credentials value, or error if the credentials Value failed 138 // to be retrieved. 139 // 140 // Will return the cached credentials Value if it has not expired. If the 141 // credentials Value has expired the Provider's Retrieve() will be called 142 // to refresh the credentials. 143 // 144 // If Credentials.Expire() was called the credentials Value will be force 145 // expired, and the next call to Get() will cause them to be refreshed. 146 func (c *Credentials) Get() (Value, error) { 147 if c == nil { 148 return Value{}, nil 149 } 150 151 c.Lock() 152 defer c.Unlock() 153 154 if c.isExpired() { 155 creds, err := c.provider.Retrieve() 156 if err != nil { 157 return Value{}, err 158 } 159 c.creds = creds 160 c.forceRefresh = false 161 } 162 163 return c.creds, nil 164 } 165 166 // Expire expires the credentials and forces them to be retrieved on the 167 // next call to Get(). 168 // 169 // This will override the Provider's expired state, and force Credentials 170 // to call the Provider's Retrieve(). 171 func (c *Credentials) Expire() { 172 c.Lock() 173 defer c.Unlock() 174 175 c.forceRefresh = true 176 } 177 178 // IsExpired returns if the credentials are no longer valid, and need 179 // to be refreshed. 180 // 181 // If the Credentials were forced to be expired with Expire() this will 182 // reflect that override. 183 func (c *Credentials) IsExpired() bool { 184 c.Lock() 185 defer c.Unlock() 186 187 return c.isExpired() 188 } 189 190 // isExpired helper method wrapping the definition of expired credentials. 191 func (c *Credentials) isExpired() bool { 192 return c.forceRefresh || c.provider.IsExpired() 193 }