Skip to content

Commit 5030afa

Browse files
committed
chore(allsrv): DRY auth with a basic auth middleware
This removes the duplication of code seen throughout our handlers. With our tests in place, we can refactor this safely.
1 parent 79af2cb commit 5030afa

File tree

1 file changed

+21
-29
lines changed

1 file changed

+21
-29
lines changed

allsrv/server.go

Lines changed: 21 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
Concerns:
1515
1) the server depends on a hard type, coupling to the exact inmem db
1616
a) what happens if we want a different db?
17-
2) auth is copy-pasted in each handler
17+
2) auth is copy-pasted in each handler
1818
a) what happens if we forget that copy pasta?
1919
3) auth is hardcoded to basic auth
2020
a) what happens if we want to adapt some other means of auth?
@@ -81,11 +81,13 @@ func NewServer(db *InmemDB, user, pass string, opts ...func(*Server)) *Server {
8181
}
8282

8383
func (s *Server) routes() {
84+
authMW := BasicAuth(s.user, s.pass) // 2)
85+
8486
// 4) 7) 9) 10)
85-
s.mux.Handle("POST /foo", http.HandlerFunc(s.createFoo))
86-
s.mux.Handle("GET /foo", http.HandlerFunc(s.readFoo))
87-
s.mux.Handle("PUT /foo", http.HandlerFunc(s.updateFoo))
88-
s.mux.Handle("DELETE /foo", http.HandlerFunc(s.delFoo))
87+
s.mux.Handle("POST /foo", authMW(http.HandlerFunc(s.createFoo)))
88+
s.mux.Handle("GET /foo", authMW(http.HandlerFunc(s.readFoo)))
89+
s.mux.Handle("PUT /foo", authMW(http.HandlerFunc(s.updateFoo)))
90+
s.mux.Handle("DELETE /foo", authMW(http.HandlerFunc(s.delFoo)))
8991
}
9092

9193
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@@ -101,12 +103,6 @@ type Foo struct {
101103
}
102104

103105
func (s *Server) createFoo(w http.ResponseWriter, r *http.Request) {
104-
// 2)
105-
if user, pass, ok := r.BasicAuth(); !(ok && user == s.user && pass == s.pass) {
106-
w.WriteHeader(http.StatusUnauthorized) // 9)
107-
return
108-
}
109-
110106
var f Foo
111107
if err := json.NewDecoder(r.Body).Decode(&f); err != nil {
112108
w.WriteHeader(http.StatusForbidden) // 9)
@@ -127,12 +123,6 @@ func (s *Server) createFoo(w http.ResponseWriter, r *http.Request) {
127123
}
128124

129125
func (s *Server) readFoo(w http.ResponseWriter, r *http.Request) {
130-
// 2)
131-
if user, pass, ok := r.BasicAuth(); !(ok && user == s.user && pass == s.pass) {
132-
w.WriteHeader(http.StatusUnauthorized) // 9)
133-
return
134-
}
135-
136126
f, err := s.db.readFoo(r.URL.Query().Get("id"))
137127
if err != nil {
138128
w.WriteHeader(http.StatusNotFound) // 9)
@@ -145,12 +135,6 @@ func (s *Server) readFoo(w http.ResponseWriter, r *http.Request) {
145135
}
146136

147137
func (s *Server) updateFoo(w http.ResponseWriter, r *http.Request) {
148-
// 2)
149-
if user, pass, ok := r.BasicAuth(); !(ok && user == s.user && pass == s.pass) {
150-
w.WriteHeader(http.StatusUnauthorized) // 9)
151-
return
152-
}
153-
154138
var f Foo
155139
if err := json.NewDecoder(r.Body).Decode(&f); err != nil {
156140
w.WriteHeader(http.StatusForbidden) // 9)
@@ -164,18 +148,26 @@ func (s *Server) updateFoo(w http.ResponseWriter, r *http.Request) {
164148
}
165149

166150
func (s *Server) delFoo(w http.ResponseWriter, r *http.Request) {
167-
// 2)
168-
if user, pass, ok := r.BasicAuth(); !(ok && user == s.user && pass == s.pass) {
169-
w.WriteHeader(http.StatusUnauthorized) // 9)
170-
return
171-
}
172-
173151
if err := s.db.delFoo(r.URL.Query().Get("id")); err != nil {
174152
w.WriteHeader(http.StatusNotFound) // 9)
175153
return
176154
}
177155
}
178156

157+
// BasicAuth provides a basic auth middleware to an http server.
158+
// 2)
159+
func BasicAuth(expectedUser, expectedPass string) func(http.Handler) http.Handler {
160+
return func(next http.Handler) http.Handler {
161+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
162+
if user, pass, ok := r.BasicAuth(); !(ok && user == expectedUser && pass == expectedPass) {
163+
w.WriteHeader(http.StatusUnauthorized) // 9)
164+
return
165+
}
166+
next.ServeHTTP(w, r)
167+
})
168+
}
169+
}
170+
179171
// InmemDB is an in-memory store.
180172
type InmemDB struct {
181173
m []Foo // 12)

0 commit comments

Comments
 (0)