forked from openshift/origin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
multi.go
105 lines (87 loc) · 3.07 KB
/
multi.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package tokencmd
import (
"net/http"
"github.com/golang/glog"
apierrs "k8s.io/kubernetes/pkg/api/errors"
utilerrors "k8s.io/kubernetes/pkg/util/errors"
)
var _ = ChallengeHandler(&MultiHandler{})
// MultiHandler manages a series of authentication challenges
// it is single-use only, and not thread-safe
type MultiHandler struct {
// handler holds the selected handler.
// automatically populated with the first handler to successfully respond to HandleChallenge(),
// and used exclusively by CanHandle() and HandleChallenge() from that point forward.
handler ChallengeHandler
// possibleHandlers holds handlers that could handle subsequent challenges.
// filtered down during HandleChallenge() by calling CanHandle() on each item.
possibleHandlers []ChallengeHandler
// allHandlers holds all handlers, for purposes of delegating Release() calls
allHandlers []ChallengeHandler
}
func NewMultiHandler(handlers ...ChallengeHandler) ChallengeHandler {
return &MultiHandler{
possibleHandlers: handlers,
allHandlers: handlers,
}
}
func (h *MultiHandler) CanHandle(headers http.Header) bool {
// If we've already selected a handler, it alone can decide whether we can handle the current request
if h.handler != nil {
return h.handler.CanHandle(headers)
}
// Otherwise, return true if any of our handlers can handle this request
for _, handler := range h.possibleHandlers {
if handler.CanHandle(headers) {
return true
}
}
return false
}
func (h *MultiHandler) HandleChallenge(requestURL string, headers http.Header) (http.Header, bool, error) {
// If we've already selected a handler, it alone can handle all subsequent challenges (don't change horses in mid-stream)
if h.handler != nil {
return h.handler.HandleChallenge(requestURL, headers)
}
// Otherwise, filter our list of handlers to the ones that can handle this request
applicable := []ChallengeHandler{}
for _, handler := range h.possibleHandlers {
if handler.CanHandle(headers) {
applicable = append(applicable, handler)
}
}
h.possibleHandlers = applicable
// Then select the first available handler that successfully handles the request
var (
retryHeaders http.Header
retry bool
err error
)
for i, handler := range h.possibleHandlers {
retryHeaders, retry, err = handler.HandleChallenge(requestURL, headers)
if err != nil {
glog.V(5).Infof("handler[%d] error: %v", i, err)
}
// If the handler successfully handled the challenge, or we have no other options, select it as our handler
if err == nil || i == len(h.possibleHandlers)-1 {
h.handler = handler
return retryHeaders, retry, err
}
}
return nil, false, apierrs.NewUnauthorized("unhandled challenge")
}
func (h *MultiHandler) CompleteChallenge(requestURL string, headers http.Header) error {
if h.handler != nil {
return h.handler.CompleteChallenge(requestURL, headers)
}
return nil
}
func (h *MultiHandler) Release() error {
var errs []error
for _, handler := range h.allHandlers {
if err := handler.Release(); err != nil {
errs = append(errs, err)
}
}
return utilerrors.NewAggregate(errs)
}