-
Notifications
You must be signed in to change notification settings - Fork 41
/
http.go
130 lines (102 loc) · 2.69 KB
/
http.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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package request
import (
"context"
"fmt"
h "net/http"
"strings"
"github.com/dgrijalva/jwt-go"
authenticationv1 "k8s.io/api/authentication/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
type authType int
const (
bearerBased authType = iota
certificateBased
anonymousBased
)
type http struct {
*h.Request
usernameClaimField string
client client.Client
}
func NewHTTP(request *h.Request, usernameClaimField string, client client.Client) Request {
return &http{Request: request, usernameClaimField: usernameClaimField, client: client}
}
func (h http) GetUserAndGroups() (username string, groups []string, err error) {
switch h.getAuthType() {
case certificateBased:
if pc := h.TLS.PeerCertificates; len(pc) == 1 {
username, groups = pc[0].Subject.CommonName, pc[0].Subject.Organization
}
case bearerBased:
if h.isJwtToken() {
return h.processJwtClaims()
}
return h.processBearerToken()
case anonymousBased:
return
}
return
}
func (h http) processJwtClaims() (username string, groups []string, err error) {
claims := h.getJwtClaims()
u, ok := claims[h.usernameClaimField]
if !ok {
return "", nil, fmt.Errorf("missing groups claim in JWT")
}
username = u.(string)
g, ok := claims["groups"]
if !ok {
return "", nil, fmt.Errorf("missing groups claim in JWT")
}
for _, v := range g.([]interface{}) {
groups = append(groups, v.(string))
}
return username, groups, nil
}
func (h http) processBearerToken() (username string, groups []string, err error) {
token := h.bearerToken()
tr := &authenticationv1.TokenReview{
Spec: authenticationv1.TokenReviewSpec{
Token: token,
},
}
if err = h.client.Create(context.Background(), tr); err != nil {
return "", nil, fmt.Errorf("cannot create TokenReview")
}
if statusErr := tr.Status.Error; len(statusErr) > 0 {
return "", nil, fmt.Errorf("cannot verify the token due to error")
}
return tr.Status.User.Username, tr.Status.User.Groups, nil
}
func (h http) bearerToken() string {
return strings.ReplaceAll(h.Header.Get("Authorization"), "Bearer ", "")
}
func (h http) getAuthType() authType {
switch {
case len(h.bearerToken()) > 0:
return bearerBased
case h.TLS != nil:
return certificateBased
default:
return anonymousBased
}
}
func (h http) getJwtClaims() jwt.MapClaims {
parser := jwt.Parser{
SkipClaimsValidation: true,
}
var token *jwt.Token
var err error
if token, _, err = parser.ParseUnverified(h.bearerToken(), jwt.MapClaims{}); err != nil {
panic(err)
}
return token.Claims.(jwt.MapClaims)
}
func (h http) isJwtToken() bool {
parser := jwt.Parser{
SkipClaimsValidation: true,
}
_, _, err := parser.ParseUnverified(h.bearerToken(), jwt.MapClaims{})
return err == nil
}