| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 } |
| OLD | NEW |