forked from npolar/gouncer
/
authenticator.go
147 lines (119 loc) · 3.9 KB
/
authenticator.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
package gouncer
import (
"encoding/json"
"github.com/npolar/toki"
"net/http"
"time"
)
type Authenticator struct {
Credentials
*Token
*ResponseHandler
}
func NewAuthenticator(h *ResponseHandler) *Authenticator {
return &Authenticator{ResponseHandler: h}
}
// HandleTokenRequest does a header check. If authorization is present it will call
// ProcessTokenRequest to handle futher validation. If no authorization header is present
// it will respond with an unauthorized error
func (auth *Authenticator) HandleTokenRequest() {
if err := auth.ParseAuthHeader(auth.HttpRequest.Header.Get("Authorization")); err == nil {
auth.ProcessTokenRequest()
} else {
auth.NewError(http.StatusUnauthorized, err.Error())
}
}
// ProcessTokenRequest retrieves the requested user and checks if the credentials match. If everything
// checks out it calls the TokenResponse to generate the actual response
func (auth *Authenticator) ProcessTokenRequest() {
if valid, err := auth.ValidCredentials(); valid {
auth.TokenResponse(auth.UserInfo)
} else {
auth.NewError(http.StatusUnauthorized, err.Error())
}
}
// TokenResponse uses the toki JWT generator library to create a new JWT.
// The resulting JWT is then set as the resonse token
func (auth *Authenticator) TokenResponse(userInfo map[string]interface{}) {
auth.GenerateSecret()
tokenizer := toki.NewJsonWebToken()
tokenizer.TokenAlgorithm = auth.ResolveAlgorithm()
// Set the token contents
tokenizer.Claim.Content = auth.TokenBody(userInfo)
// Sign the token and return the full token string
tokenizer.Sign(auth.Secret)
token, err := tokenizer.String()
if err == nil {
// Cache the token info for validation purposes
err = auth.CacheTokenInfo()
// When token info is cached respond with the token
if err == nil {
auth.Response.Status = http.StatusOK
auth.Response.Token = token
}
}
if err != nil {
auth.NewError(http.StatusUnauthorized, err.Error())
}
}
func (auth *Authenticator) ResolveAlgorithm() *toki.Algorithm {
switch auth.Algorithm {
case "none":
return toki.NoAlg()
case "HS384":
return toki.HS384()
case "HS512":
return toki.HS512()
default:
return toki.HS256()
}
}
// CacheTokenInfo caches the username and secret for validation purposes
func (auth *Authenticator) CacheTokenInfo() error {
data, err := json.Marshal(&CacheObj{auth.Secret})
if err == nil {
return auth.CacheCredentials(auth.Username, data, auth.Expiration)
}
return err
}
// Generate the contents that will be sent in the tokens claim body
func (auth *Authenticator) TokenBody(userData map[string]interface{}) map[string]interface{} {
var content = make(map[string]interface{})
var systems []interface{}
content["email"] = auth.Username
if name, exists := userData["name"]; exists {
content["name"] = name
}
if uri, exists := userData["uri"]; exists {
content["uri"] = uri
}
if groups, exists := userData["groups"]; exists {
systems = auth.ResolveGroupsToSystems(groups.([]interface{}))
}
if list, exists := userData["systems"].([]interface{}); exists {
systems = auth.ResolveDuplicateSystems(list, systems)
}
if len(systems) > 0 {
content["systems"] = systems
}
content["exp"] = time.Now().Add(time.Duration(auth.Expiration) * time.Second).Unix() // Move expiration control to the token
return content
}
func (auth *Authenticator) ResolveDuplicateSystems(userSystems []interface{}, systems []interface{}) []interface{} {
for l, uSys := range userSystems {
accessible := true
// Check if the system already exists and override if found
for i, system := range systems {
if system.(map[string]interface{})["uri"] == uSys.(map[string]interface{})["uri"] {
systems[i] = uSys
userSystems = append(userSystems[:l], userSystems[l+1:]...) // When overriding remove the item from the list
accessible = false
}
}
// If non existent append the system into the list
if accessible {
systems = append(systems, uSys)
}
}
return systems
}