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

Side by Side Diff: server/auth/auth.go

Issue 2830443003: auth: Refactor how authentication methods are passed to server/auth library. (Closed)
Patch Set: Created 3 years, 8 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
OLDNEW
1 // Copyright 2015 The LUCI Authors. All rights reserved. 1 // Copyright 2015 The LUCI Authors. All rights reserved.
2 // Use of this source code is governed under the Apache License, Version 2.0 2 // Use of this source code is governed under the Apache License, Version 2.0
3 // that can be found in the LICENSE file. 3 // that can be found in the LICENSE file.
4 4
5 package auth 5 package auth
6 6
7 import ( 7 import (
8 "fmt" 8 "fmt"
9 "net/http" 9 "net/http"
10 10
(...skipping 18 matching lines...) Expand all
29 29
30 // ErrBadClientID is returned by Authenticate if caller is using 30 // ErrBadClientID is returned by Authenticate if caller is using
31 // non-whitelisted OAuth2 client. More info is in the log. 31 // non-whitelisted OAuth2 client. More info is in the log.
32 ErrBadClientID = errors.New("auth: OAuth client_id is not whitelisted") 32 ErrBadClientID = errors.New("auth: OAuth client_id is not whitelisted")
33 33
34 // ErrIPNotWhitelisted is returned when an account is restricted by an I P 34 // ErrIPNotWhitelisted is returned when an account is restricted by an I P
35 // whitelist and request's remote_addr is not in it. 35 // whitelist and request's remote_addr is not in it.
36 ErrIPNotWhitelisted = errors.New("auth: IP is not whitelisted") 36 ErrIPNotWhitelisted = errors.New("auth: IP is not whitelisted")
37 ) 37 )
38 38
39 // Method implements particular kind of low level authentication mechanism for 39 // Method implements a particular kind of low-level authentication mechanism.
40 // incoming requests. It may also optionally implement UsersAPI (if the method 40 //
41 // support login and logout URLs). Use type sniffing to figure out. 41 // It may also optionally implement UsersAPI (if the method support login and
42 // logout URLs).
43 //
44 // Methods are not usually used directly, but passed to Authenticator{...} that
45 // knows how to apply them.
42 type Method interface { 46 type Method interface {
43 // Authenticate extracts user information from the incoming request. 47 // Authenticate extracts user information from the incoming request.
48 //
44 // It returns: 49 // It returns:
45 // * (*User, nil) on success. 50 // * (*User, nil) on success.
46 // * (nil, nil) if the method is not applicable. 51 // * (nil, nil) if the method is not applicable.
47 // * (nil, error) if the method is applicable, but credentials are inv alid. 52 // * (nil, error) if the method is applicable, but credentials are inv alid.
48 Authenticate(context.Context, *http.Request) (*User, error) 53 Authenticate(context.Context, *http.Request) (*User, error)
49 } 54 }
50 55
51 // UsersAPI may be additionally implemented by Method if it supports login and 56 // UsersAPI may be additionally implemented by Method if it supports login and
52 // logout URLs. 57 // logout URLs.
53 type UsersAPI interface { 58 type UsersAPI interface {
(...skipping 27 matching lines...) Expand all
81 86
82 // Picture is URL of the user avatar. Optional, default "". 87 // Picture is URL of the user avatar. Optional, default "".
83 Picture string 88 Picture string
84 89
85 // ClientID is the ID of the pre-registered OAuth2 client so its identit y can 90 // ClientID is the ID of the pre-registered OAuth2 client so its identit y can
86 // be verified. Used only by authentication methods based on OAuth2. 91 // be verified. Used only by authentication methods based on OAuth2.
87 // See https://developers.google.com/console/help/#generatingoauth2 for more. 92 // See https://developers.google.com/console/help/#generatingoauth2 for more.
88 ClientID string 93 ClientID string
89 } 94 }
90 95
91 // Authenticator perform authentication of incoming requests. It is stateless 96 // Authenticator perform authentication of incoming requests.
92 // object that just describes what methods to try when authenticating current 97 //
93 // request. It is fine to create it on per-request basis. 98 // It is stateless object configured with a list of methods to try when
94 type Authenticator []Method 99 // authenticating incoming requests. It implements Authenticate method that
100 // performs high-level authentication logic using the provided list of low-level
101 // auth methods.
102 //
103 // Usually used through Authenticate(...) middelware.
104 type Authenticator struct {
105 » Methods []Method // a list of authentication methods to try
106 }
95 107
96 // Authenticate authenticates incoming requests and returns new context.Context 108 // Authenticate authenticates the requests and adds State into the context.
97 // with State stored into it. Returns error if credentials are provided, but 109 //
98 // invalid. If no credentials are provided (i.e. the request is anonymous), 110 // Returns an error if credentials are provided, but invalid. If no credentials
99 // finishes successfully, but in that case State.Identity() will return 111 // are provided (i.e. the request is anonymous), finishes successfully, but in
100 // AnonymousIdentity. 112 // that case State.Identity() returns AnonymousIdentity.
101 func (a Authenticator) Authenticate(c context.Context, r *http.Request) (context .Context, error) { 113 func (a *Authenticator) Authenticate(c context.Context, r *http.Request) (contex t.Context, error) {
102 report := durationReporter(c, authenticateDuration) 114 report := durationReporter(c, authenticateDuration)
103 115
104 // Make it known to handlers what authentication methods are allowed.
105 // This is important for LoginURL and LogoutURL functions.
106 c = SetAuthenticator(c, a)
107
108 // We will need working DB factory below to check IP whitelist. 116 // We will need working DB factory below to check IP whitelist.
109 cfg := GetConfig(c) 117 cfg := GetConfig(c)
110 if cfg == nil || cfg.DBProvider == nil { 118 if cfg == nil || cfg.DBProvider == nil {
111 report(ErrNotConfigured, "ERROR_NOT_CONFIGURED") 119 report(ErrNotConfigured, "ERROR_NOT_CONFIGURED")
112 return nil, ErrNotConfigured 120 return nil, ErrNotConfigured
113 } 121 }
114 122
115 // Pick first authentication method that applies. 123 // Pick first authentication method that applies.
116 » var s state 124 » s := state{authenticator: a}
117 » for _, m := range a { 125 » for _, m := range a.Methods {
118 var err error 126 var err error
119 s.user, err = m.Authenticate(c, r) 127 s.user, err = m.Authenticate(c, r)
120 if err != nil { 128 if err != nil {
121 report(err, "ERROR_BROKEN_CREDS") // e.g. malformed OAut h token 129 report(err, "ERROR_BROKEN_CREDS") // e.g. malformed OAut h token
122 return nil, err 130 return nil, err
123 } 131 }
124 if s.user != nil { 132 if s.user != nil {
125 if err = s.user.Identity.Validate(); err != nil { 133 if err = s.user.Identity.Validate(); err != nil {
126 report(err, "ERROR_BROKEN_IDENTITY") // a weird looking email address 134 report(err, "ERROR_BROKEN_IDENTITY") // a weird looking email address
127 return nil, err 135 return nil, err
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after
230 "peerID": s.peerIdent, 238 "peerID": s.peerIdent,
231 "delegatedID": delegatedIdentity, 239 "delegatedID": delegatedIdentity,
232 }.Debugf(c, "auth: Using delegation") 240 }.Debugf(c, "auth: Using delegation")
233 } 241 }
234 242
235 // Inject auth state. 243 // Inject auth state.
236 report(nil, "SUCCESS") 244 report(nil, "SUCCESS")
237 return WithState(c, &s), nil 245 return WithState(c, &s), nil
238 } 246 }
239 247
240 // usersAPI returns implementation of UsersAPI by examining Methods. Returns nil 248 // usersAPI returns implementation of UsersAPI by examining Methods.
241 // if none of Methods implement UsersAPI. 249 //
242 func (a Authenticator) usersAPI() UsersAPI { 250 // Returns nil if none of Methods implement UsersAPI.
243 » for _, m := range a { 251 func (a *Authenticator) usersAPI() UsersAPI {
252 » for _, m := range a.Methods {
244 if api, ok := m.(UsersAPI); ok { 253 if api, ok := m.(UsersAPI); ok {
245 return api 254 return api
246 } 255 }
247 } 256 }
248 return nil 257 return nil
249 } 258 }
250 259
251 // LoginURL returns a URL that, when visited, prompts the user to sign in, 260 // LoginURL returns a URL that, when visited, prompts the user to sign in,
252 // then redirects the user to the URL specified by dest. 261 // then redirects the user to the URL specified by dest.
253 func (a Authenticator) LoginURL(c context.Context, dest string) (string, error) { 262 //
263 // Returns ErrNoUsersAPI if none of the authentication methods support login
264 // URLs.
265 func (a *Authenticator) LoginURL(c context.Context, dest string) (string, error) {
254 if api := a.usersAPI(); api != nil { 266 if api := a.usersAPI(); api != nil {
255 return api.LoginURL(c, dest) 267 return api.LoginURL(c, dest)
256 } 268 }
257 return "", ErrNoUsersAPI 269 return "", ErrNoUsersAPI
258 } 270 }
259 271
260 // LogoutURL returns a URL that, when visited, signs the user out, 272 // LogoutURL returns a URL that, when visited, signs the user out, then
261 // then redirects the user to the URL specified by dest. 273 // redirects the user to the URL specified by dest.
262 func (a Authenticator) LogoutURL(c context.Context, dest string) (string, error) { 274 //
275 // Returns ErrNoUsersAPI if none of the authentication methods support login
276 // URLs.
277 func (a *Authenticator) LogoutURL(c context.Context, dest string) (string, error ) {
263 if api := a.usersAPI(); api != nil { 278 if api := a.usersAPI(); api != nil {
264 return api.LogoutURL(c, dest) 279 return api.LogoutURL(c, dest)
265 } 280 }
266 return "", ErrNoUsersAPI 281 return "", ErrNoUsersAPI
267 } 282 }
268 283
269 //// 284 ////
270 285
271 // getOwnServiceIdentity returns 'service:<appID>' identity of the current 286 // getOwnServiceIdentity returns 'service:<appID>' identity of the current
272 // service. 287 // service.
273 func getOwnServiceIdentity(c context.Context, signer signing.Signer) (identity.I dentity, error) { 288 func getOwnServiceIdentity(c context.Context, signer signing.Signer) (identity.I dentity, error) {
274 if signer == nil { 289 if signer == nil {
275 return "", ErrNotConfigured 290 return "", ErrNotConfigured
276 } 291 }
277 serviceInfo, err := signer.ServiceInfo(c) 292 serviceInfo, err := signer.ServiceInfo(c)
278 if err != nil { 293 if err != nil {
279 return "", err 294 return "", err
280 } 295 }
281 return identity.MakeIdentity("service:" + serviceInfo.AppID) 296 return identity.MakeIdentity("service:" + serviceInfo.AppID)
282 } 297 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698