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
This commit is contained in:
Kurt Jung 2017-02-17 17:37:58 -05:00 committed by Matt Holt
parent 82cbd7a96b
commit 977a3c3226
3 changed files with 28 additions and 5 deletions

View File

@ -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))
}
}

View File

@ -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)

View File

@ -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,