| OLD | NEW |
| (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 package auth | |
| 6 | |
| 7 import ( | |
| 8 "io/ioutil" | |
| 9 "net/http" | |
| 10 "os" | |
| 11 "path/filepath" | |
| 12 "testing" | |
| 13 | |
| 14 "infra/libs/auth/internal" | |
| 15 "infra/libs/logging" | |
| 16 | |
| 17 "golang.org/x/net/context" | |
| 18 | |
| 19 . "github.com/smartystreets/goconvey/convey" | |
| 20 ) | |
| 21 | |
| 22 var ( | |
| 23 ctx = context.Background() | |
| 24 log = logging.Null() | |
| 25 ) | |
| 26 | |
| 27 func ExampleDefaultAuthenticatedClient() { | |
| 28 client, err := AuthenticatedClient(SilentLogin, NewAuthenticator(Options
{})) | |
| 29 if err == ErrLoginRequired { | |
| 30 log.Errorf("Run 'auth login' to login") | |
| 31 return | |
| 32 } | |
| 33 if err != nil { | |
| 34 log.Errorf("Failed to login: %s", err) | |
| 35 return | |
| 36 } | |
| 37 client.Get("https://some-server.appspot.com") | |
| 38 } | |
| 39 | |
| 40 func mockSecretsDir() string { | |
| 41 tempDir, err := ioutil.TempDir("", "auth_test") | |
| 42 So(err, ShouldBeNil) | |
| 43 | |
| 44 prev := secretsDir | |
| 45 secretsDir = func() string { return tempDir } | |
| 46 Reset(func() { | |
| 47 secretsDir = prev | |
| 48 os.RemoveAll(tempDir) | |
| 49 }) | |
| 50 So(SecretsDir(), ShouldEqual, tempDir) | |
| 51 | |
| 52 return tempDir | |
| 53 } | |
| 54 | |
| 55 func mockTokenProvider(factory func() internal.TokenProvider) { | |
| 56 prev := makeTokenProvider | |
| 57 makeTokenProvider = func(*Options) (internal.TokenProvider, error) { | |
| 58 return factory(), nil | |
| 59 } | |
| 60 Reset(func() { | |
| 61 makeTokenProvider = prev | |
| 62 }) | |
| 63 } | |
| 64 | |
| 65 func TestAuthenticator(t *testing.T) { | |
| 66 Convey("Given mocked secrets dir", t, func() { | |
| 67 tempDir := mockSecretsDir() | |
| 68 | |
| 69 Convey("Check NewAuthenticator defaults", func() { | |
| 70 clientID, clientSecret := DefaultClient() | |
| 71 ctx := context.Background() | |
| 72 a := NewAuthenticator(Options{Context: ctx}).(*authentic
atorImpl) | |
| 73 So(a.opts, ShouldResemble, &Options{ | |
| 74 Method: AutoSelectMethod, | |
| 75 Scopes: []string{OAuthScopeEmail
}, | |
| 76 ClientID: clientID, | |
| 77 ClientSecret: clientSecret, | |
| 78 ServiceAccountJSONPath: filepath.Join(tempDir, "
service_account.json"), | |
| 79 GCEAccountName: "default", | |
| 80 Context: ctx, | |
| 81 Logger: logging.Get(ctx), | |
| 82 }) | |
| 83 }) | |
| 84 }) | |
| 85 } | |
| 86 | |
| 87 func TestAuthenticatedClient(t *testing.T) { | |
| 88 Convey("Given mocked secrets dir", t, func() { | |
| 89 var tokenProvider internal.TokenProvider | |
| 90 | |
| 91 mockSecretsDir() | |
| 92 mockTokenProvider(func() internal.TokenProvider { return tokenPr
ovider }) | |
| 93 | |
| 94 Convey("Test login required", func() { | |
| 95 tokenProvider = &fakeTokenProvider{interactive: true} | |
| 96 c, err := AuthenticatedClient(InteractiveLogin, NewAuthe
nticator(Options{})) | |
| 97 So(err, ShouldBeNil) | |
| 98 So(c, ShouldNotEqual, http.DefaultClient) | |
| 99 }) | |
| 100 | |
| 101 Convey("Test login not required", func() { | |
| 102 tokenProvider = &fakeTokenProvider{interactive: true} | |
| 103 c, err := AuthenticatedClient(OptionalLogin, NewAuthenti
cator(Options{})) | |
| 104 So(err, ShouldBeNil) | |
| 105 So(c, ShouldEqual, http.DefaultClient) | |
| 106 }) | |
| 107 }) | |
| 108 } | |
| 109 | |
| 110 func TestRefreshToken(t *testing.T) { | |
| 111 Convey("Given mocked secrets dir", t, func() { | |
| 112 var tokenProvider *fakeTokenProvider | |
| 113 | |
| 114 mockSecretsDir() | |
| 115 mockTokenProvider(func() internal.TokenProvider { return tokenPr
ovider }) | |
| 116 | |
| 117 Convey("Test non interactive auth", func() { | |
| 118 tokenProvider = &fakeTokenProvider{ | |
| 119 interactive: false, | |
| 120 tokenToMint: &fakeToken{}, | |
| 121 } | |
| 122 auth, ok := NewAuthenticator(Options{}).(*authenticatorI
mpl) | |
| 123 So(ok, ShouldBeTrue) | |
| 124 _, err := auth.Transport() | |
| 125 So(err, ShouldBeNil) | |
| 126 // No token yet. The token is minted on first refresh. | |
| 127 So(auth.currentToken(), ShouldBeNil) | |
| 128 tok, err := auth.refreshToken(nil) | |
| 129 So(err, ShouldBeNil) | |
| 130 So(tok, ShouldEqual, tokenProvider.tokenToMint) | |
| 131 }) | |
| 132 | |
| 133 Convey("Test interactive auth (cache expired)", func() { | |
| 134 tokenProvider = &fakeTokenProvider{ | |
| 135 interactive: true, | |
| 136 tokenToMint: &fakeToken{name: "minted"}, | |
| 137 tokenToRefresh: &fakeToken{name: "refreshed"}, | |
| 138 tokenToUnmarshal: &fakeToken{name: "cached", exp
ired: true}, | |
| 139 } | |
| 140 auth, ok := NewAuthenticator(Options{}).(*authenticatorI
mpl) | |
| 141 So(ok, ShouldBeTrue) | |
| 142 _, err := auth.Transport() | |
| 143 So(err, ShouldEqual, ErrLoginRequired) | |
| 144 err = auth.Login() | |
| 145 So(err, ShouldBeNil) | |
| 146 _, err = auth.Transport() | |
| 147 So(err, ShouldBeNil) | |
| 148 // Minted initial token. | |
| 149 So(auth.currentToken(), ShouldEqual, tokenProvider.token
ToMint) | |
| 150 // Should return refreshed token. | |
| 151 tok, err := auth.refreshToken(auth.currentToken()) | |
| 152 So(err, ShouldBeNil) | |
| 153 So(tok, ShouldEqual, tokenProvider.tokenToRefresh) | |
| 154 }) | |
| 155 | |
| 156 Convey("Test interactive auth (cache non expired)", func() { | |
| 157 tokenProvider = &fakeTokenProvider{ | |
| 158 interactive: true, | |
| 159 tokenToMint: &fakeToken{name: "minted"}, | |
| 160 tokenToRefresh: &fakeToken{name: "refreshed"}, | |
| 161 tokenToUnmarshal: &fakeToken{name: "cached", exp
ired: false}, | |
| 162 } | |
| 163 auth, ok := NewAuthenticator(Options{}).(*authenticatorI
mpl) | |
| 164 So(ok, ShouldBeTrue) | |
| 165 _, err := auth.Transport() | |
| 166 So(err, ShouldEqual, ErrLoginRequired) | |
| 167 err = auth.Login() | |
| 168 So(err, ShouldBeNil) | |
| 169 _, err = auth.Transport() | |
| 170 So(err, ShouldBeNil) | |
| 171 // Minted initial token. | |
| 172 So(auth.currentToken(), ShouldEqual, tokenProvider.token
ToMint) | |
| 173 // Should return token from cache (since it's not expire
d yet). | |
| 174 tok, err := auth.refreshToken(auth.currentToken()) | |
| 175 So(err, ShouldBeNil) | |
| 176 So(tok, ShouldEqual, tokenProvider.tokenToUnmarshal) | |
| 177 }) | |
| 178 }) | |
| 179 } | |
| 180 | |
| 181 //////////////////////////////////////////////////////////////////////////////// | |
| 182 | |
| 183 type fakeTokenProvider struct { | |
| 184 interactive bool | |
| 185 tokenToMint internal.Token | |
| 186 tokenToRefresh internal.Token | |
| 187 tokenToUnmarshal internal.Token | |
| 188 } | |
| 189 | |
| 190 func (p *fakeTokenProvider) RequiresInteraction() bool { | |
| 191 return p.interactive | |
| 192 } | |
| 193 | |
| 194 func (p *fakeTokenProvider) MintToken() (internal.Token, error) { | |
| 195 if p.tokenToMint != nil { | |
| 196 return p.tokenToMint, nil | |
| 197 } | |
| 198 return &fakeToken{}, nil | |
| 199 } | |
| 200 | |
| 201 func (p *fakeTokenProvider) RefreshToken(internal.Token) (internal.Token, error)
{ | |
| 202 if p.tokenToRefresh != nil { | |
| 203 return p.tokenToRefresh, nil | |
| 204 } | |
| 205 return &fakeToken{}, nil | |
| 206 } | |
| 207 | |
| 208 func (p *fakeTokenProvider) MarshalToken(internal.Token) ([]byte, error) { | |
| 209 return []byte("fake token"), nil | |
| 210 } | |
| 211 | |
| 212 func (p *fakeTokenProvider) UnmarshalToken([]byte) (internal.Token, error) { | |
| 213 if p.tokenToUnmarshal != nil { | |
| 214 return p.tokenToUnmarshal, nil | |
| 215 } | |
| 216 return &fakeToken{}, nil | |
| 217 } | |
| 218 | |
| 219 type fakeToken struct { | |
| 220 name string | |
| 221 expired bool | |
| 222 } | |
| 223 | |
| 224 func (t *fakeToken) Equals(another internal.Token) bool { | |
| 225 casted, ok := another.(*fakeToken) | |
| 226 return ok && casted == t | |
| 227 } | |
| 228 | |
| 229 func (t *fakeToken) RequestHeaders() map[string]string { return make(map[string]
string) } | |
| 230 func (t *fakeToken) Expired() bool { return t.expired } | |
| OLD | NEW |