-
Notifications
You must be signed in to change notification settings - Fork 9
/
auth.go
182 lines (159 loc) · 5.07 KB
/
auth.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
package jwt
import (
"fmt"
"strings"
"time"
authorization "github.com/travelgateX/go-jwt-tools"
"github.com/form3tech-oss/jwt-go"
)
var _ authorization.Parser = (*Parser)(nil)
type Parser struct {
client client
KeyFunc func(token *jwt.Token) (interface{}, error)
ParserConfig
}
// ParserConfig is the data required to instance a Parser
type ParserConfig struct {
ClientConfig *ClientConfig `json:"client_config"`
PublicKey string `json:"public_key_str"`
AdminGroup string `json:"admin_group"`
DummyToken string `json:"dummy_token"`
Expiration string `json:"exp"`
IsExpired bool `json:"is_expired"`
MemberIDClaim []string `json:"member_id_claim"`
GroupsClaim []string `json:"groups_claim"`
FetchNeededClaim []string `json:"fetch_needed_claim"`
TGXMemberClaim []string `json:"tgx_member_claim"`
OrganizationsClaim []string `json:"organizations_claim"`
IgnoreExpiration bool `json:"ignore_expiration"`
DisableFetchNeeded bool `json:"disable_fetch_needed"`
}
type ClientConfig struct {
FetcherURL string `json:"fetcher_url"`
}
func (c ClientConfig) buildClient() client {
return newClient(c.FetcherURL)
}
// NewParser returns an instance of Parser which parses bearers from a publicKey
func NewParser(p ParserConfig) *Parser {
var client client
if p.ClientConfig != nil {
client = p.ClientConfig.buildClient()
}
jkf := func(token *jwt.Token) (interface{}, error) {
var result interface{}
result, err := jwt.ParseRSAPublicKeyFromPEM([]byte(p.PublicKey))
return result, err
}
return &Parser{
KeyFunc: jkf,
ParserConfig: p,
client: client,
}
}
func (p *Parser) Parse(authorizationHeader string) (*authorization.User, error) {
// validate bearer
authorizationHeaderParts := strings.SplitN(authorizationHeader, " ", 2)
if len(authorizationHeaderParts) != 2 || authorizationHeaderParts[0] != "Bearer" {
return nil, fmt.Errorf("authorization header format must be Bearer {token}")
}
// dummy treatment
if p.DummyToken != "" && authorizationHeaderParts[1] == p.DummyToken {
return &authorization.User{
AuthorizationValue: authorizationHeader,
IsDummy: true,
Permissions: nil, // TODO: NoopImpl?
}, nil
}
// parse token
jwtp := &jwt.Parser{SkipClaimsValidation: p.IgnoreExpiration}
token, err := jwtp.Parse(authorizationHeaderParts[1], p.KeyFunc)
if err != nil {
return nil, fmt.Errorf("error parsing bearer: %v", err)
}
if jwt.SigningMethodRS256.Alg() != token.Header["alg"] {
message := fmt.Sprintf("Expected %s signing method but token specified %s",
jwt.SigningMethodRS256.Alg(),
token.Header["alg"])
return nil, fmt.Errorf("error validating token algorithm: %s", message)
}
// check if the parsed token is valid...
if !token.Valid {
return nil, authorization.ErrInvalidUser
}
return p.createUser(token)
}
func (p *Parser) createUser(token *jwt.Token) (*authorization.User, error) {
claimsMap := token.Claims.(jwt.MapClaims)
isTgxMember := false
for _, f := range p.TGXMemberClaim {
if c, ok := claimsMap[f]; ok {
if c.(bool) {
isTgxMember = true
}
}
}
if !p.DisableFetchNeeded {
// First of all is checked if the token received in a "fullToken"
for _, f := range p.FetchNeededClaim {
if c, ok := claimsMap[f]; ok {
if c.(bool) {
if p.client != nil {
// Get the client's "fullToken"
shortToken := "Bearer " + token.Raw
fullBearer, err := p.client.GetBearer("", shortToken)
if err != nil {
return nil, err
}
// Do Parse(), recursive call with the new authorization token
user, err := p.Parse("Bearer " + fullBearer)
if err != nil {
return nil, err
}
// Set the reduced token in the response object
user.AuthorizationValue = shortToken
user.TgxMember = isTgxMember
return user, nil
}
}
}
}
}
// This way is done when the token received is a "fullToken"
// TODO: remove when migration finishes
groups := make([]interface{}, 0, len(p.GroupsClaim))
for _, g := range p.GroupsClaim {
if c, ok := claimsMap[g]; ok {
groups = append(groups, c)
}
}
memberIDs := make([]string, 0, len(p.MemberIDClaim))
for _, m := range p.MemberIDClaim {
if c, ok := claimsMap[m]; ok {
if mID, ok := c.(string); ok {
memberIDs = append(memberIDs, mID)
}
}
}
organizations := make([]interface{}, 0, len(p.OrganizationsClaim))
for _, g := range p.OrganizationsClaim {
if c, ok := claimsMap[g]; ok {
organizations = append(organizations, c)
}
}
exp := claimsMap["exp"]
return &authorization.User{
AuthorizationValue: "Bearer " + token.Raw,
IsDummy: false,
Permissions: NewPermissions(groups, memberIDs, p.AdminGroup),
UserID: memberIDs,
TgxMember: isTgxMember,
IsExpired: isExpired(exp.(float64)),
Expiration: exp.(float64),
Orgs: organizations,
}, nil
}
func isExpired(exp float64) bool {
expDate := time.Unix(int64(exp), 0)
return expDate.Before(time.Now())
}