| OLD | NEW |
| 1 // Copyright 2017 The LUCI Authors. All rights reserved. | 1 // Copyright 2017 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 localauth | 5 package localauth |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "crypto/subtle" | 8 "crypto/subtle" |
| 9 "encoding/json" | 9 "encoding/json" |
| 10 "fmt" | 10 "fmt" |
| 11 "io" | 11 "io" |
| 12 "mime" | 12 "mime" |
| 13 "net" | 13 "net" |
| 14 "net/http" | 14 "net/http" |
| 15 "regexp" | 15 "regexp" |
| 16 "sort" | 16 "sort" |
| 17 "sync" | 17 "sync" |
| 18 "time" | 18 "time" |
| 19 | 19 |
| 20 "golang.org/x/net/context" | 20 "golang.org/x/net/context" |
| 21 "golang.org/x/oauth2" | 21 "golang.org/x/oauth2" |
| 22 | 22 |
| 23 "github.com/luci/luci-go/common/auth/localauth/rpcs" | 23 "github.com/luci/luci-go/common/auth/localauth/rpcs" |
| 24 "github.com/luci/luci-go/common/data/rand/cryptorand" | 24 "github.com/luci/luci-go/common/data/rand/cryptorand" |
| 25 "github.com/luci/luci-go/common/data/stringset" | 25 "github.com/luci/luci-go/common/data/stringset" |
| 26 "github.com/luci/luci-go/common/errors" | |
| 27 "github.com/luci/luci-go/common/logging" | 26 "github.com/luci/luci-go/common/logging" |
| 27 "github.com/luci/luci-go/common/retry" |
| 28 "github.com/luci/luci-go/common/runtime/paniccatcher" | 28 "github.com/luci/luci-go/common/runtime/paniccatcher" |
| 29 "github.com/luci/luci-go/lucictx" | 29 "github.com/luci/luci-go/lucictx" |
| 30 ) | 30 ) |
| 31 | 31 |
| 32 // TokenGenerator produces access tokens. | 32 // TokenGenerator produces access tokens. |
| 33 // | 33 // |
| 34 // It is called to return an access token for given combination of scopes (given | 34 // It is called to return an access token for given combination of scopes (given |
| 35 // as a sorted list of strings without duplicates). | 35 // as a sorted list of strings without duplicates). |
| 36 // | 36 // |
| 37 // It is called for each request to the local auth server. It may be called | 37 // It is called for each request to the local auth server. It may be called |
| 38 // concurrently from multiple goroutines and must implement its own caching and | 38 // concurrently from multiple goroutines and must implement its own caching and |
| 39 // synchronization if necessary. | 39 // synchronization if necessary. |
| 40 // | 40 // |
| 41 // It is expected that the returned token lives for at least given 'lifetime' | 41 // It is expected that the returned token lives for at least given 'lifetime' |
| 42 // duration (which is typically on order of minutes), but it may live longer. | 42 // duration (which is typically on order of minutes), but it may live longer. |
| 43 // Clients may cache the returned token for the duration of its lifetime. | 43 // Clients may cache the returned token for the duration of its lifetime. |
| 44 // | 44 // |
| 45 // May return transient errors (in errors.IsTransient returning true sense). | 45 // May return transient errors (in retry.Tag.In(err) returning true |
| 46 // Such errors result in HTTP 500 responses. This is appropriate for non-fatal | 46 // sense). Such errors result in HTTP 500 responses. This is appropriate for |
| 47 // errors. Clients may immediately retry requests on such errors. | 47 // non-fatal errors. Clients may immediately retry requests on such errors. |
| 48 // | 48 // |
| 49 // Any non-transient error is considered fatal and results in an RPC-level | 49 // Any non-transient error is considered fatal and results in an RPC-level |
| 50 // error response ({"error": ...}). Clients must treat such responses as fatal | 50 // error response ({"error": ...}). Clients must treat such responses as fatal |
| 51 // and don't retry requests. | 51 // and don't retry requests. |
| 52 // | 52 // |
| 53 // If the error implements ErrorWithCode interface, the error code returned to | 53 // If the error implements ErrorWithCode interface, the error code returned to |
| 54 // clients will be grabbed from the error object, otherwise the error code is | 54 // clients will be grabbed from the error object, otherwise the error code is |
| 55 // set to -1. | 55 // set to -1. |
| 56 type TokenGenerator func(ctx context.Context, scopes []string, lifetime time.Dur
ation) (*oauth2.Token, error) | 56 type TokenGenerator func(ctx context.Context, scopes []string, lifetime time.Dur
ation) (*oauth2.Token, error) |
| 57 | 57 |
| (...skipping 270 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 328 // Route to the appropriate RPC handler. | 328 // Route to the appropriate RPC handler. |
| 329 response, err := h.routeToImpl(method, request) | 329 response, err := h.routeToImpl(method, request) |
| 330 | 330 |
| 331 // *protocolError are sent as HTTP errors. | 331 // *protocolError are sent as HTTP errors. |
| 332 if pErr, _ := err.(*protocolError); pErr != nil { | 332 if pErr, _ := err.(*protocolError); pErr != nil { |
| 333 http.Error(rw, pErr.Message, pErr.Status) | 333 http.Error(rw, pErr.Message, pErr.Status) |
| 334 return | 334 return |
| 335 } | 335 } |
| 336 | 336 |
| 337 // Transient errors are returned as HTTP 500 responses. | 337 // Transient errors are returned as HTTP 500 responses. |
| 338 » if errors.IsTransient(err) { | 338 » if retry.Tag.In(err) { |
| 339 http.Error(rw, fmt.Sprintf("Transient error - %s", err), http.St
atusInternalServerError) | 339 http.Error(rw, fmt.Sprintf("Transient error - %s", err), http.St
atusInternalServerError) |
| 340 return | 340 return |
| 341 } | 341 } |
| 342 | 342 |
| 343 // Fatal errors are returned as specially structured JSON responses with | 343 // Fatal errors are returned as specially structured JSON responses with |
| 344 // HTTP 200 code. Replace 'response' with it. | 344 // HTTP 200 code. Replace 'response' with it. |
| 345 if err != nil { | 345 if err != nil { |
| 346 fatalError := rpcs.BaseResponse{ | 346 fatalError := rpcs.BaseResponse{ |
| 347 ErrorCode: -1, | 347 ErrorCode: -1, |
| 348 ErrorMessage: err.Error(), | 348 ErrorMessage: err.Error(), |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 433 // Ask the token provider for the token. This may produce ErrorWithCode. | 433 // Ask the token provider for the token. This may produce ErrorWithCode. |
| 434 tok, err := generator(h.ctx, sortedScopes, minTokenLifetime) | 434 tok, err := generator(h.ctx, sortedScopes, minTokenLifetime) |
| 435 if err != nil { | 435 if err != nil { |
| 436 return nil, err | 436 return nil, err |
| 437 } | 437 } |
| 438 return &rpcs.GetOAuthTokenResponse{ | 438 return &rpcs.GetOAuthTokenResponse{ |
| 439 AccessToken: tok.AccessToken, | 439 AccessToken: tok.AccessToken, |
| 440 Expiry: tok.Expiry.Unix(), | 440 Expiry: tok.Expiry.Unix(), |
| 441 }, nil | 441 }, nil |
| 442 } | 442 } |
| OLD | NEW |