-
Notifications
You must be signed in to change notification settings - Fork 2
/
frontends.go
159 lines (141 loc) · 5.29 KB
/
frontends.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
package authaus
import (
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"net/http"
"strings"
)
var (
ErrHttpBasicAuth = errors.New("HTTP Basic Authorization must be base64(identity:password)")
ErrHttpNotAuthorized = errors.New("No authorization information")
)
// HttpHandlerPrelude reads the session cookie or the HTTP "Basic" Authorization header to determine whether this request is authorized.
func HttpHandlerPrelude(config *ConfigHTTP, central *Central, r *http.Request) (*Token, error) {
sessioncookie, _ := r.Cookie(config.CookieName)
if sessioncookie != nil {
return central.GetTokenFromSession(sessioncookie.Value)
} else {
return HttpHandlerBasicAuth(central, r)
}
}
func HttpHandlerBasicAuth(central *Central, r *http.Request) (*Token, error) {
auth := r.Header.Get("Authorization")
if auth == "" {
return nil, ErrHttpNotAuthorized
}
identity, password, basicOK := r.BasicAuth()
if !basicOK {
return nil, ErrHttpBasicAuth
} else {
return central.GetTokenFromIdentityPassword(identity, password)
}
}
// Runs the Prelude function, but before returning an error, sends an appropriate error response to the HTTP ResponseWriter.
// If this function returns a non-nil error, then it means that you should not send anything else to the http response.
func HttpHandlerPreludeWithError(config *ConfigHTTP, central *Central, w http.ResponseWriter, r *http.Request) (*Token, error) {
token, err := HttpHandlerPrelude(config, central, r)
if err != nil {
if strings.Index(err.Error(), ErrIdentityEmpty.Error()) == 0 {
HttpSendTxt(w, http.StatusUnauthorized, err.Error())
} else if err == ErrHttpBasicAuth {
HttpSendTxt(w, http.StatusBadRequest, err.Error())
} else if err == ErrHttpNotAuthorized {
HttpSendTxt(w, http.StatusUnauthorized, err.Error())
} else {
HttpSendTxt(w, http.StatusForbidden, err.Error())
}
}
return token, err
}
// HttpHandlerWhoAmI handles the 'whoami' request, which is really just for debugging
func HttpHandlerWhoAmI(config *ConfigHTTP, central *Central, w http.ResponseWriter, r *http.Request) {
token, err := HttpHandlerPrelude(config, central, r)
if err != nil {
HttpSendTxt(w, http.StatusForbidden, err.Error())
} else {
HttpSendTxt(w, http.StatusOK, fmt.Sprintf("Success: Roles=%v", hex.EncodeToString(token.Permit.Roles)))
}
}
func HttpNoCache(w http.ResponseWriter) {
w.Header().Add("Cache-Control", "no-cache, no-store, must revalidate")
w.Header().Add("Pragma", "no-cache")
w.Header().Add("Expires", "0")
}
func HttpSendTxt(w http.ResponseWriter, responseCode int, responseBody string) {
HttpNoCache(w)
w.Header().Add("Content-Type", "text/plain")
w.WriteHeader(responseCode)
w.Write([]byte(responseBody))
}
func HttpSendHTML(w http.ResponseWriter, responseCode int, responseBody string) {
HttpNoCache(w)
w.Header().Add("Content-Type", "text/html")
w.WriteHeader(responseCode)
w.Write([]byte(responseBody))
}
func HttpSendJSON(w http.ResponseWriter, responseCode int, responseObject interface{}) {
HttpNoCache(w)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(responseCode)
enc := json.NewEncoder(w)
enc.Encode(responseObject)
}
// HttpHandlerLogin handles the 'login' request, sending back a session token (via Set-Cookie),
// if authentication succeeds. You may want to use this as a template to write your own.
func HttpHandlerLogin(config *ConfigHTTP, central *Central, w http.ResponseWriter, r *http.Request) {
identity, password, basicOK := r.BasicAuth()
if !basicOK {
HttpSendTxt(w, http.StatusBadRequest, ErrHttpBasicAuth.Error())
return
}
if sessionkey, token, err := central.Login(identity, password, ""); err != nil {
HttpSendTxt(w, http.StatusForbidden, err.Error())
} else {
cookie := &http.Cookie{
Name: config.CookieName,
Value: sessionkey,
Path: "/",
Expires: token.Expires,
Secure: config.CookieSecure,
}
http.SetCookie(w, cookie)
w.WriteHeader(http.StatusOK)
}
}
func HttpHandlerLogout(config *ConfigHTTP, central *Central, w http.ResponseWriter, r *http.Request) {
sessioncookie, _ := r.Cookie(config.CookieName)
if sessioncookie != nil {
err := central.Logout(sessioncookie.Value)
if err != nil {
HttpSendTxt(w, http.StatusServiceUnavailable, err.Error())
}
}
HttpSendTxt(w, http.StatusOK, "")
}
// Run as a standalone HTTP server. This just wires up the various HTTP handler functions and starts
// a listener. You will probably want to add your own entry points and do that yourself instead of using this.
// This function is useful for demo/example purposes.
func RunHttp(config *ConfigHTTP, central *Central) error {
makehandler := func(actual func(*ConfigHTTP, *Central, http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
actual(config, central, w, r)
}
}
http.HandleFunc("/whoami", makehandler(HttpHandlerWhoAmI))
http.HandleFunc("/login", makehandler(HttpHandlerLogin))
http.HandleFunc("/logout", makehandler(HttpHandlerLogout))
fmt.Printf("Trying to listen on %v:%v\n", config.Bind, config.Port)
if err := http.ListenAndServe(config.Bind+":"+config.Port, nil); err != nil {
return err
}
return nil
}
func RunHttpFromConfig(config *Config) error {
if central, err := NewCentralFromConfig(config); err != nil {
return err
} else {
return RunHttp(&config.HTTP, central)
}
}