Skip to content

Commit 2416f89

Browse files
authored
Merge pull request #2480 from mtrmac/token-cleanup
Clean up obtaining bearer tokens for registries
2 parents 7ce6348 + d6f4f35 commit 2416f89

File tree

2 files changed

+55
-45
lines changed

2 files changed

+55
-45
lines changed

docker/docker_client.go

+45-36
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,9 @@ type extensionSignatureList struct {
8686
Signatures []extensionSignature `json:"signatures"`
8787
}
8888

89+
// bearerToken records a cached token we can use to authenticate.
8990
type bearerToken struct {
90-
Token string `json:"token"`
91-
AccessToken string `json:"access_token"`
92-
ExpiresIn int `json:"expires_in"`
93-
IssuedAt time.Time `json:"issued_at"`
91+
token string
9492
expirationTime time.Time
9593
}
9694

@@ -147,37 +145,6 @@ const (
147145
noAuth
148146
)
149147

150-
// newBearerTokenFromHTTPResponseBody parses a http.Response to obtain a bearerToken.
151-
// The caller is still responsible for ensuring res.Body is closed.
152-
func newBearerTokenFromHTTPResponseBody(res *http.Response) (*bearerToken, error) {
153-
blob, err := iolimits.ReadAtMost(res.Body, iolimits.MaxAuthTokenBodySize)
154-
if err != nil {
155-
return nil, err
156-
}
157-
158-
token := new(bearerToken)
159-
if err := json.Unmarshal(blob, &token); err != nil {
160-
const bodySampleLength = 50
161-
bodySample := blob
162-
if len(bodySample) > bodySampleLength {
163-
bodySample = bodySample[:bodySampleLength]
164-
}
165-
return nil, fmt.Errorf("decoding bearer token (last URL %q, body start %q): %w", res.Request.URL.Redacted(), string(bodySample), err)
166-
}
167-
if token.Token == "" {
168-
token.Token = token.AccessToken
169-
}
170-
if token.ExpiresIn < minimumTokenLifetimeSeconds {
171-
token.ExpiresIn = minimumTokenLifetimeSeconds
172-
logrus.Debugf("Increasing token expiration to: %d seconds", token.ExpiresIn)
173-
}
174-
if token.IssuedAt.IsZero() {
175-
token.IssuedAt = time.Now().UTC()
176-
}
177-
token.expirationTime = token.IssuedAt.Add(time.Duration(token.ExpiresIn) * time.Second)
178-
return token, nil
179-
}
180-
181148
// dockerCertDir returns a path to a directory to be consumed by tlsclientconfig.SetupCertificates() depending on ctx and hostPort.
182149
func dockerCertDir(sys *types.SystemContext, hostPort string) (string, error) {
183150
if sys != nil && sys.DockerCertPath != "" {
@@ -786,7 +753,7 @@ func (c *dockerClient) setupRequestAuth(req *http.Request, extraScope *authScope
786753
token = *t
787754
c.tokenCache.Store(cacheKey, token)
788755
}
789-
registryToken = token.Token
756+
registryToken = token.token
790757
}
791758
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", registryToken))
792759
return nil
@@ -889,6 +856,48 @@ func (c *dockerClient) getBearerToken(ctx context.Context, challenge challenge,
889856
return newBearerTokenFromHTTPResponseBody(res)
890857
}
891858

859+
// newBearerTokenFromHTTPResponseBody parses a http.Response to obtain a bearerToken.
860+
// The caller is still responsible for ensuring res.Body is closed.
861+
func newBearerTokenFromHTTPResponseBody(res *http.Response) (*bearerToken, error) {
862+
blob, err := iolimits.ReadAtMost(res.Body, iolimits.MaxAuthTokenBodySize)
863+
if err != nil {
864+
return nil, err
865+
}
866+
867+
var token struct {
868+
Token string `json:"token"`
869+
AccessToken string `json:"access_token"`
870+
ExpiresIn int `json:"expires_in"`
871+
IssuedAt time.Time `json:"issued_at"`
872+
expirationTime time.Time
873+
}
874+
if err := json.Unmarshal(blob, &token); err != nil {
875+
const bodySampleLength = 50
876+
bodySample := blob
877+
if len(bodySample) > bodySampleLength {
878+
bodySample = bodySample[:bodySampleLength]
879+
}
880+
return nil, fmt.Errorf("decoding bearer token (last URL %q, body start %q): %w", res.Request.URL.Redacted(), string(bodySample), err)
881+
}
882+
883+
bt := &bearerToken{
884+
token: token.Token,
885+
}
886+
if bt.token == "" {
887+
bt.token = token.AccessToken
888+
}
889+
890+
if token.ExpiresIn < minimumTokenLifetimeSeconds {
891+
token.ExpiresIn = minimumTokenLifetimeSeconds
892+
logrus.Debugf("Increasing token expiration to: %d seconds", token.ExpiresIn)
893+
}
894+
if token.IssuedAt.IsZero() {
895+
token.IssuedAt = time.Now().UTC()
896+
}
897+
bt.expirationTime = token.IssuedAt.Add(time.Duration(token.ExpiresIn) * time.Second)
898+
return bt, nil
899+
}
900+
892901
// detectPropertiesHelper performs the work of detectProperties which executes
893902
// it at most once.
894903
func (c *dockerClient) detectPropertiesHelper(ctx context.Context) error {

docker/docker_client_test.go

+10-9
Original file line numberDiff line numberDiff line change
@@ -117,26 +117,25 @@ func TestNewBearerTokenFromHTTPResponseBody(t *testing.T) {
117117
},
118118
{ // "token"
119119
input: `{"token":"IAmAToken","expires_in":100,"issued_at":"2018-01-01T10:00:02+00:00"}`,
120-
expected: &bearerToken{Token: "IAmAToken", ExpiresIn: 100, IssuedAt: time.Unix(1514800802, 0)},
120+
expected: &bearerToken{token: "IAmAToken", expirationTime: time.Unix(1514800802+100, 0)},
121121
},
122122
{ // "access_token"
123123
input: `{"access_token":"IAmAToken","expires_in":100,"issued_at":"2018-01-01T10:00:02+00:00"}`,
124-
expected: &bearerToken{Token: "IAmAToken", ExpiresIn: 100, IssuedAt: time.Unix(1514800802, 0)},
124+
expected: &bearerToken{token: "IAmAToken", expirationTime: time.Unix(1514800802+100, 0)},
125125
},
126126
{ // Small expiry
127127
input: `{"token":"IAmAToken","expires_in":1,"issued_at":"2018-01-01T10:00:02+00:00"}`,
128-
expected: &bearerToken{Token: "IAmAToken", ExpiresIn: 60, IssuedAt: time.Unix(1514800802, 0)},
128+
expected: &bearerToken{token: "IAmAToken", expirationTime: time.Unix(1514800802+60, 0)},
129129
},
130130
} {
131131
token, err := newBearerTokenFromHTTPResponseBody(testTokenHTTPResponse(t, c.input))
132132
if c.expected == nil {
133133
assert.Error(t, err, c.input)
134134
} else {
135135
require.NoError(t, err, c.input)
136-
assert.Equal(t, c.expected.Token, token.Token, c.input)
137-
assert.Equal(t, c.expected.ExpiresIn, token.ExpiresIn, c.input)
138-
assert.True(t, c.expected.IssuedAt.Equal(token.IssuedAt),
139-
"expected [%s] to equal [%s], it did not", token.IssuedAt, c.expected.IssuedAt)
136+
assert.Equal(t, c.expected.token, token.token, c.input)
137+
assert.True(t, c.expected.expirationTime.Equal(token.expirationTime),
138+
"expected [%s] to equal [%s], it did not", token.expirationTime, c.expected.expirationTime)
140139
}
141140
}
142141
}
@@ -145,9 +144,11 @@ func TestNewBearerTokenFromHTTPResponseBodyIssuedAtZero(t *testing.T) {
145144
zeroTime := time.Time{}.Format(time.RFC3339)
146145
now := time.Now()
147146
tokenBlob := fmt.Sprintf(`{"token":"IAmAToken","expires_in":100,"issued_at":"%s"}`, zeroTime)
148-
token, err := newBearerTokenFromHTTPResponseBody(testTokenHTTPResponse(t, string(tokenBlob)))
147+
token, err := newBearerTokenFromHTTPResponseBody(testTokenHTTPResponse(t, tokenBlob))
149148
require.NoError(t, err)
150-
assert.False(t, token.IssuedAt.Before(now), "expected [%s] not to be before [%s]", token.IssuedAt, now)
149+
expectedExpiration := now.Add(time.Duration(100) * time.Second)
150+
require.False(t, token.expirationTime.Before(expectedExpiration),
151+
"expected [%s] not to be before [%s]", token.expirationTime, expectedExpiration)
151152
}
152153

153154
func TestUserAgent(t *testing.T) {

0 commit comments

Comments
 (0)