Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(29)

Side by Side Diff: go/src/infra/libs/auth/internal/common.go

Issue 1153883002: go: infra/libs/* now live in luci-go. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: move the rest too Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « go/src/infra/libs/auth/cli.go ('k') | go/src/infra/libs/auth/internal/gce.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /*
6 Package internal contains interfaces and structs used internally
7 by infra.libs.auth. They are moved to a separate package for a better
8 readability of main infra.libs.auth code: infra.libs.auth implements top level
9 logic, infra.libs.auth.internal implements dirty details.
10 */
11 package internal
12
13 import (
14 "encoding/json"
15 "errors"
16 "fmt"
17 "net/http"
18 "time"
19
20 "golang.org/x/net/context"
21 "golang.org/x/oauth2"
22 )
23
24 // ErrInsufficientAccess is can't be minted for given OAuth scopes. For example
25 // if GCE instance wasn't granted access to requested scopes when it was created .
26 var ErrInsufficientAccess = errors.New("Can't get access token for given scopes" )
27
28 // Token is immutable object that internally holds authentication credentials
29 // (short term, long term, or both). Token knows how to modify http.Request to
30 // apply the credentials to it. TokenProvider acts as a factory for Tokens.
31 type Token interface {
32 // Equals compares this token to another token of the same kind.
33 Equals(Token) bool
34
35 // RequestHeaders returns a map of authorization headers to add to the r equest.
36 RequestHeaders() map[string]string
37
38 // Expired is true if token MUST be refreshed via RefreshToken call.
39 Expired() bool
40 }
41
42 // TokenProvider knows how to mint new tokens, refresh existing ones, marshal
43 // and umarshal tokens to byte buffer. TokenProvider acts as a factory for
44 // particular implementations of Token interface. Tokens from two different
45 // provider instances must not be mixed together.
46 type TokenProvider interface {
47 // RequiresInteraction is true if provider may start user interaction in MintToken.
48 RequiresInteraction() bool
49
50 // MintToken launches authentication flow (possibly interactive) and ret urns
51 // a new refreshable token.
52 MintToken() (Token, error)
53
54 // RefreshToken takes existing token (probably expired, but not necessar ily)
55 // and returns a new refreshed token. It should never do any user intera ction.
56 // If a user interaction is required, a error should be returned instead .
57 RefreshToken(Token) (Token, error)
58
59 // MarshalToken converts a token to byte buffer.
60 MarshalToken(Token) ([]byte, error)
61
62 // UnmarshalToken takes byte buffer produced by MarshalToken and returns
63 // original Token.
64 UnmarshalToken([]byte) (Token, error)
65 }
66
67 // TransportFromContext returns http.RoundTripper buried inside the given
68 // context.
69 func TransportFromContext(ctx context.Context) http.RoundTripper {
70 // When nil is passed to NewClient it skips all OAuth stuff and returns
71 // client extracted from the context or http.DefaultClient.
72 c := oauth2.NewClient(ctx, nil)
73 if c == http.DefaultClient {
74 return http.DefaultTransport
75 }
76 return c.Transport
77 }
78
79 ///////////////////////////////////////////////////////////////////////////////
80
81 // tokenImpl implements Token interface by adapting oauth2.Token.
82 type tokenImpl struct {
83 oauth2.Token
84 }
85
86 // makeToken builds Token from oauth2.Token by copying it.
87 func makeToken(tok *oauth2.Token) Token {
88 return &tokenImpl{
89 Token: *tok,
90 }
91 }
92
93 // extractOAuthToken takes Token, checks its type and return oauth2.Token.
94 // It returns a copy that can be safely mutated.
95 func extractOAuthToken(tok Token) oauth2.Token {
96 // It's OK to panic here on type mismatch.
97 return tok.(*tokenImpl).Token
98 }
99
100 func (t *tokenImpl) Equals(another Token) bool {
101 if another == nil {
102 return false
103 }
104 casted, ok := another.(*tokenImpl)
105 if !ok {
106 return false
107 }
108 return t.AccessToken == casted.AccessToken
109 }
110
111 func (t *tokenImpl) Expired() bool {
112 if t.AccessToken == "" {
113 return true
114 }
115 if t.Expiry.IsZero() {
116 return false
117 }
118 // Allow 1 min clock skew.
119 expiry := t.Expiry.Add(-time.Minute)
120 return expiry.Before(time.Now())
121 }
122
123 func (t *tokenImpl) RequestHeaders() map[string]string {
124 ret := make(map[string]string)
125 if t.AccessToken != "" {
126 ret["Authorization"] = "Bearer " + t.AccessToken
127 }
128 return ret
129 }
130
131 ///////////////////////////////////////////////////////////////////////////////
132
133 // oauthTokenProvider partially implements a TokenProvider built on top of oauth
134 // library. Concrete implementations embed this struct and provide missing
135 // MintToken and RefreshToken methods.
136 type oauthTokenProvider struct {
137 interactive bool
138 tokenFlavor string
139 }
140
141 type tokenOnDisk struct {
142 Version string `json:"version"`
143 Flavor string `json:"flavor"`
144 AccessToken string `json:"access_token"`
145 RefreshToken string `json:"refresh_token,omitempty"`
146 ExpiresAtSec int64 `json:"expires_at,omitempty"`
147 }
148
149 const tokFormatVersion = "1"
150
151 func (p *oauthTokenProvider) RequiresInteraction() bool {
152 return p.interactive
153 }
154
155 func (p *oauthTokenProvider) MarshalToken(t Token) ([]byte, error) {
156 tok := extractOAuthToken(t)
157 return json.Marshal(&tokenOnDisk{
158 Version: tokFormatVersion,
159 Flavor: p.tokenFlavor,
160 AccessToken: tok.AccessToken,
161 RefreshToken: tok.RefreshToken,
162 ExpiresAtSec: tok.Expiry.Unix(),
163 })
164 }
165
166 func (p *oauthTokenProvider) UnmarshalToken(data []byte) (Token, error) {
167 onDisk := tokenOnDisk{}
168 if err := json.Unmarshal(data, &onDisk); err != nil {
169 return nil, err
170 }
171 if onDisk.Version != tokFormatVersion {
172 return nil, fmt.Errorf("auth: bad token version %q, expected %q" , onDisk.Version, tokFormatVersion)
173 }
174 if onDisk.Flavor != p.tokenFlavor {
175 return nil, fmt.Errorf("auth: bad token flavor %q, expected %q", onDisk.Flavor, p.tokenFlavor)
176 }
177 return makeToken(&oauth2.Token{
178 AccessToken: onDisk.AccessToken,
179 RefreshToken: onDisk.RefreshToken,
180 Expiry: time.Unix(onDisk.ExpiresAtSec, 0),
181 }), nil
182 }
OLDNEW
« no previous file with comments | « go/src/infra/libs/auth/cli.go ('k') | go/src/infra/libs/auth/internal/gce.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698