Skip to content

Commit 1a259d6

Browse files
committed
chore(allsrv): inject authorization mechanism to decouple it from server
This does a few things: 1) Allows us to control the auth at setup without having to update the implementation of the server endpoints. We've effectively decoupled our auth from the server, which gives us freedom to adapt to future asks. 2) The injection is using a `middleware` function. This could be an interface as well. Its totally up to the developer/team. Sometimes an interface is more useful. 3) We have the freedom to ignore auth in tests if we so desire. This can be useful if your auth setup is non-trivial and involves a good bit of complexity.
1 parent 5030afa commit 1a259d6

File tree

2 files changed

+35
-21
lines changed

2 files changed

+35
-21
lines changed

allsrv/server.go

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
a) what happens if we want a different db?
1717
✅2) auth is copy-pasted in each handler
1818
a) what happens if we forget that copy pasta?
19-
3) auth is hardcoded to basic auth
19+
3) auth is hardcoded to basic auth
2020
a) what happens if we want to adapt some other means of auth?
2121
✅4) router being used is the GLOBAL http.DefaultServeMux
2222
a) should avoid globals
@@ -49,9 +49,16 @@ type Server struct {
4949
db *InmemDB // 1)
5050
mux *http.ServeMux // 4)
5151

52-
user, pass string // 3)
52+
authFn func(http.Handler) http.Handler // 3)
53+
idFn func() string // 11)
54+
}
5355

54-
idFn func() string // 11)
56+
// WithBasicAuth sets the authorization fn for the server to basic auth.
57+
// 3)
58+
func WithBasicAuth(user, pass string) func(*Server) {
59+
return func(s *Server) {
60+
s.authFn = basicAuth(user, pass)
61+
}
5562
}
5663

5764
// WithIDFn sets the id generation fn for the server.
@@ -61,12 +68,16 @@ func WithIDFn(fn func() string) func(*Server) {
6168
}
6269
}
6370

64-
func NewServer(db *InmemDB, user, pass string, opts ...func(*Server)) *Server {
71+
func NewServer(db *InmemDB, opts ...func(*Server)) *Server {
6572
s := Server{
66-
db: db,
67-
mux: http.NewServeMux(), // 4)
68-
user: user,
69-
pass: pass,
73+
db: db,
74+
mux: http.NewServeMux(), // 4)
75+
authFn: func(next http.Handler) http.Handler { // 3)
76+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
77+
// defaults to no auth
78+
next.ServeHTTP(w, r)
79+
})
80+
},
7081
idFn: func() string {
7182
// defaults to using a uuid
7283
return uuid.Must(uuid.NewV4()).String()
@@ -81,7 +92,7 @@ func NewServer(db *InmemDB, user, pass string, opts ...func(*Server)) *Server {
8192
}
8293

8394
func (s *Server) routes() {
84-
authMW := BasicAuth(s.user, s.pass) // 2)
95+
authMW := s.authFn // 2)
8596

8697
// 4) 7) 9) 10)
8798
s.mux.Handle("POST /foo", authMW(http.HandlerFunc(s.createFoo)))
@@ -154,9 +165,9 @@ func (s *Server) delFoo(w http.ResponseWriter, r *http.Request) {
154165
}
155166
}
156167

157-
// BasicAuth provides a basic auth middleware to an http server.
168+
// basicAuth provides a basic auth middleware to an http server.
158169
// 2)
159-
func BasicAuth(expectedUser, expectedPass string) func(http.Handler) http.Handler {
170+
func basicAuth(expectedUser, expectedPass string) func(http.Handler) http.Handler {
160171
return func(next http.Handler) http.Handler {
161172
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
162173
if user, pass, ok := r.BasicAuth(); !(ok && user == expectedUser && pass == expectedPass) {

allsrv/server_test.go

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@ func TestServer(t *testing.T) {
1818
t.Run("foo create", func(t *testing.T) {
1919
t.Run("when provided a valid foo should pass", func(t *testing.T) {
2020
db := new(allsrv.InmemDB)
21-
svr := allsrv.NewServer(db, "dodgers@stink.com", "PaSsWoRd", allsrv.WithIDFn(func() string {
22-
return "id1"
23-
}))
21+
svr := allsrv.NewServer(db,
22+
allsrv.WithBasicAuth("dodgers@stink.com", "PaSsWoRd"),
23+
allsrv.WithIDFn(func() string {
24+
return "id1"
25+
}),
26+
)
2427

2528
req := httptest.NewRequest("POST", "/foo", newJSONBody(t, allsrv.Foo{
2629
Name: "first-foo",
@@ -43,7 +46,7 @@ func TestServer(t *testing.T) {
4346
})
4447

4548
t.Run("when provided invalid basic auth should fail", func(t *testing.T) {
46-
svr := allsrv.NewServer(new(allsrv.InmemDB), "dodgers@stink.com", "PaSsWoRd")
49+
svr := allsrv.NewServer(new(allsrv.InmemDB), allsrv.WithBasicAuth("dodgers@stink.com", "PaSsWoRd"))
4750

4851
req := httptest.NewRequest("POST", "/foo", newJSONBody(t, allsrv.Foo{
4952
Name: "first-foo",
@@ -68,7 +71,7 @@ func TestServer(t *testing.T) {
6871
})
6972
require.NoError(t, err)
7073

71-
svr := allsrv.NewServer(db, "dodgers@stink.com", "PaSsWoRd")
74+
svr := allsrv.NewServer(db, allsrv.WithBasicAuth("dodgers@stink.com", "PaSsWoRd"))
7275

7376
req := httptest.NewRequest("GET", "/foo?id=reader1", nil)
7477
req.SetBasicAuth("dodgers@stink.com", "PaSsWoRd")
@@ -88,7 +91,7 @@ func TestServer(t *testing.T) {
8891
})
8992

9093
t.Run("when provided invalid basic auth should fail", func(t *testing.T) {
91-
svr := allsrv.NewServer(new(allsrv.InmemDB), "dodgers@stink.com", "PaSsWoRd")
94+
svr := allsrv.NewServer(new(allsrv.InmemDB), allsrv.WithBasicAuth("dodgers@stink.com", "PaSsWoRd"))
9295

9396
req := httptest.NewRequest("GET", "/foo?id=reader1", nil)
9497
req.SetBasicAuth("dodgers@rule.com", "wrongO")
@@ -110,7 +113,7 @@ func TestServer(t *testing.T) {
110113
})
111114
require.NoError(t, err)
112115

113-
svr := allsrv.NewServer(db, "dodgers@stink.com", "PaSsWoRd")
116+
svr := allsrv.NewServer(db, allsrv.WithBasicAuth("dodgers@stink.com", "PaSsWoRd"))
114117

115118
req := httptest.NewRequest("PUT", "/foo", newJSONBody(t, allsrv.Foo{
116119
ID: "id1",
@@ -135,7 +138,7 @@ func TestServer(t *testing.T) {
135138
})
136139
require.NoError(t, err)
137140

138-
svr := allsrv.NewServer(db, "dodgers@stink.com", "PaSsWoRd")
141+
svr := allsrv.NewServer(db, allsrv.WithBasicAuth("dodgers@stink.com", "PaSsWoRd"))
139142

140143
req := httptest.NewRequest("PUT", "/foo", newJSONBody(t, allsrv.Foo{
141144
ID: "id1",
@@ -161,7 +164,7 @@ func TestServer(t *testing.T) {
161164
})
162165
require.NoError(t, err)
163166

164-
svr := allsrv.NewServer(db, "dodgers@stink.com", "PaSsWoRd")
167+
svr := allsrv.NewServer(db, allsrv.WithBasicAuth("dodgers@stink.com", "PaSsWoRd"))
165168

166169
req := httptest.NewRequest("DELETE", "/foo?id=id1", nil)
167170
req.SetBasicAuth("dodgers@stink.com", "PaSsWoRd")
@@ -173,7 +176,7 @@ func TestServer(t *testing.T) {
173176
})
174177

175178
t.Run("when provided invalid basic auth should fail", func(t *testing.T) {
176-
svr := allsrv.NewServer(new(allsrv.InmemDB), "dodgers@stink.com", "PaSsWoRd")
179+
svr := allsrv.NewServer(new(allsrv.InmemDB), allsrv.WithBasicAuth("dodgers@stink.com", "PaSsWoRd"))
177180

178181
req := httptest.NewRequest("DELETE", "/foo?id=id1", nil)
179182
req.SetBasicAuth("dodgers@rule.com", "wrongO")

0 commit comments

Comments
 (0)