From 977a3c322631b41b534f079e2466689f9aa3d3f7 Mon Sep 17 00:00:00 2001 From: Kurt Jung Date: Fri, 17 Feb 2017 17:37:58 -0500 Subject: [PATCH] basicauth: Store name of authenticated user (#1426) * Store name of authenticated user in basicauth for use by upstream middleware such as fastcgi and cgi. * Use request context to transfer name of authorized user from basicauth to upstream middleware. Test retrieval of name from context. * Remove development code that was inadvertently left in place * Use keys of type httpserver.CtxKey to access Context values --- caddyhttp/basicauth/basicauth.go | 6 ++++++ caddyhttp/basicauth/basicauth_test.go | 21 +++++++++++++++++---- caddyhttp/fastcgi/fastcgi.go | 6 +++++- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/caddyhttp/basicauth/basicauth.go b/caddyhttp/basicauth/basicauth.go index eb7a3fe2..57ab563c 100644 --- a/caddyhttp/basicauth/basicauth.go +++ b/caddyhttp/basicauth/basicauth.go @@ -7,6 +7,7 @@ package basicauth import ( "bufio" + "context" "crypto/sha1" "crypto/subtle" "fmt" @@ -60,6 +61,11 @@ func (a BasicAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error // remove credentials from request to avoid leaking upstream r.Header.Del("Authorization") + + // let upstream middleware (e.g. fastcgi and cgi) know about authenticated + // user; this replaces the request with a wrapped instance + r = r.WithContext(context.WithValue(r.Context(), + httpserver.CtxKey("remote_user"), username)) } } diff --git a/caddyhttp/basicauth/basicauth_test.go b/caddyhttp/basicauth/basicauth_test.go index 9097b088..c90bf12b 100644 --- a/caddyhttp/basicauth/basicauth_test.go +++ b/caddyhttp/basicauth/basicauth_test.go @@ -14,19 +14,31 @@ import ( ) func TestBasicAuth(t *testing.T) { + var i int + // This handler is registered for tests in which the only authorized user is + // "okuser" + upstreamHandler := func(w http.ResponseWriter, r *http.Request) (int, error) { + remoteUser, _ := r.Context().Value(httpserver.CtxKey("remote_user")).(string) + if remoteUser != "okuser" { + t.Errorf("Test %d: expecting remote user 'okuser', got '%s'", i, remoteUser) + } + return http.StatusOK, nil + } rw := BasicAuth{ - Next: httpserver.HandlerFunc(contentHandler), + Next: httpserver.HandlerFunc(upstreamHandler), Rules: []Rule{ {Username: "okuser", Password: PlainMatcher("okpass"), Resources: []string{"/testing"}}, }, } - tests := []struct { + type testType struct { from string result int user string password string - }{ + } + + tests := []testType{ {"/testing", http.StatusOK, "okuser", "okpass"}, {"/testing", http.StatusUnauthorized, "baduser", "okpass"}, {"/testing", http.StatusUnauthorized, "okuser", "badpass"}, @@ -37,7 +49,8 @@ func TestBasicAuth(t *testing.T) { {"/testing", http.StatusUnauthorized, "", ""}, } - for i, test := range tests { + var test testType + for i, test = range tests { req, err := http.NewRequest("GET", test.from, nil) if err != nil { t.Fatalf("Test %d: Could not create HTTP request: %v", i, err) diff --git a/caddyhttp/fastcgi/fastcgi.go b/caddyhttp/fastcgi/fastcgi.go index 0833274f..4bc48cca 100644 --- a/caddyhttp/fastcgi/fastcgi.go +++ b/caddyhttp/fastcgi/fastcgi.go @@ -220,6 +220,10 @@ func (h Handler) buildEnv(r *http.Request, rule Rule, fpath string) (map[string] reqURI = origURI } + // Retrieve name of remote user that was set by some downstream middleware, + // possibly basicauth. + remoteUser, _ := r.Context().Value(httpserver.CtxKey("remote_user")).(string) // Blank if not set + // Some variables are unused but cleared explicitly to prevent // the parent environment from interfering. env = map[string]string{ @@ -235,7 +239,7 @@ func (h Handler) buildEnv(r *http.Request, rule Rule, fpath string) (map[string] "REMOTE_HOST": ip, // For speed, remote host lookups disabled "REMOTE_PORT": port, "REMOTE_IDENT": "", // Not used - "REMOTE_USER": "", // Not used + "REMOTE_USER": remoteUser, "REQUEST_METHOD": r.Method, "SERVER_NAME": h.ServerName, "SERVER_PORT": h.ServerPort,