-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.go
155 lines (116 loc) · 3.54 KB
/
server.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
package main
import (
"time"
"github.com/fasthttp/router"
"github.com/valyala/fasthttp"
)
const (
// isLoggedIn is the key used in the session manager to signify whether
// the user is, uhhhh, logged in
isLoggedIn = "is-logged-in"
forwardedForHeader = "X-Forwarded-Host"
)
type Server struct {
config *Config
*router.Router
}
func New(c *Config) (s *Server, err error) {
s = new(Server)
s.config = c
s.Router = router.New()
s.GET("/", s.RenderForm)
api := s.Group("/api")
v1 := api.Group("/v1")
v1.GET("/auth", s.Auth)
v1.POST("/login", s.Login)
v1.GET("/logout", s.Logout)
return
}
// Auth determines whether a request is allowed access to the specified downstream
// and either:
//
// 1. Returns a 200, signifying a request is valid; or
// 2. Returns a 303 to the login form; or finally
// 3. Returns a 404, signifying that there is no configured login portal for the forwarded host
//
// We use a 303 to ensure that the form is always requested as a GET
func (s *Server) Auth(ctx *fasthttp.RequestCtx) {
addr, vhost, err := s.config.MatchVHostByOrigin(ctx.Request.Header.Peek(forwardedForHeader))
if err != nil {
ctx.Error(err.Error(), fasthttp.StatusNotFound)
return
}
store, err := vhost.sm.Get(ctx)
if err != nil {
ctx.Error(err.Error(), fasthttp.StatusInternalServerError)
return
}
if b := store.Get(isLoggedIn); b != nil {
ctx.SetStatusCode(fasthttp.StatusOK)
return
}
ctx.Redirect(addr, fasthttp.StatusSeeOther)
}
// Login is a form handler, which receives a username and password from the login form.
//
// It looks these up against the htpasswd specified in the app config. Where the
// credentials are correct, we redirect to the original URL. Where they don't, we
// return a very unceremonious 403 message
func (s *Server) Login(ctx *fasthttp.RequestCtx) {
username := string(ctx.FormValue("username"))
password := string(ctx.FormValue("password"))
vhost, err := s.config.MatchVHost(ctx.Request.Header.Peek(forwardedForHeader))
if err != nil {
ctx.Error(err.Error(), fasthttp.StatusNotFound)
return
}
if vhost.Authenticate(username, password) {
store, err := vhost.sm.Get(ctx)
if err != nil {
ctx.Error(err.Error(), fasthttp.StatusInternalServerError)
return
}
store.Set(isLoggedIn, true)
err = vhost.sm.Save(ctx, store)
if err != nil {
ctx.Error(err.Error(), fasthttp.StatusInternalServerError)
return
}
ctx.Redirect(vhost.Redirect, fasthttp.StatusSeeOther)
return
}
time.Sleep(time.Millisecond * 300)
ctx.Error("incorrect username/password combination", fasthttp.StatusForbidden)
}
func (s *Server) Logout(ctx *fasthttp.RequestCtx) {
vhost, err := s.config.MatchVHost(ctx.Request.Header.Peek(forwardedForHeader))
if err != nil {
ctx.Error(err.Error(), fasthttp.StatusNotFound)
return
}
store, err := vhost.sm.Get(ctx)
if err != nil {
ctx.Error(err.Error(), fasthttp.StatusInternalServerError)
return
}
store.Set(isLoggedIn, false)
err = vhost.sm.Save(ctx, store)
if err != nil {
ctx.Error(err.Error(), fasthttp.StatusInternalServerError)
return
}
ctx.Redirect(vhost.Redirect, fasthttp.StatusSeeOther)
}
// RenderForm shows the login form as provided by the sysadmin
func (s *Server) RenderForm(ctx *fasthttp.RequestCtx) {
vhost, err := s.config.MatchVHost(ctx.Request.Header.Peek(forwardedForHeader))
if err != nil {
ctx.Error(err.Error(), fasthttp.StatusNotFound)
return
}
ctx.SetContentType("text/html; charset=utf8")
err = vhost.templates.Execute(ctx, "login")
if err != nil {
ctx.Error(err.Error(), fasthttp.StatusInternalServerError)
}
}