From 94bb7a14350246cd3769af0ccc4d0301932b859e Mon Sep 17 00:00:00 2001 From: Ian Date: Fri, 16 Dec 2016 07:42:51 -0800 Subject: [PATCH] Add ability to set authenticated user in request header (#889) * Add ability to set authenticated user in request header Some web applications provide the ability to authorize users based on the authenticated from Basic Auth. This patch provides a way to set a key to which the authenticated user can be set in the Header. For example, if I set `HeaderValue = "X-WebAuth-User"` and authenticate, my application will be able to read my user name from that header and provide me with the proper access. This fixes #802 --- docs/user-guide/examples.md | 16 ++++++++++++++++ middlewares/authenticator.go | 11 ++++++++++- middlewares/authenticator_test.go | 30 ++++++++++++++++++++++++++++++ types/types.go | 5 +++-- 4 files changed, 59 insertions(+), 3 deletions(-) diff --git a/docs/user-guide/examples.md b/docs/user-guide/examples.md index 3e4465247..7d807afad 100644 --- a/docs/user-guide/examples.md +++ b/docs/user-guide/examples.md @@ -114,4 +114,20 @@ defaultEntryPoints = ["http"] address = ":80" [entryPoints.http.auth.basic] users = ["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"] +``` + +## Pass Authenticated user to application via headers + +Providing an authentication method as described above, it is possible to pass the user to the application +via a configurable header value + +``` +defaultEntryPoints = ["http"] +[entryPoints] + [entryPoints.http] + address = ":80" + [entryPoints.http.auth] + headerField = "X-WebAuth-User" + [entryPoints.http.auth.basic] + users = ["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"] ``` \ No newline at end of file diff --git a/middlewares/authenticator.go b/middlewares/authenticator.go index 380287e3f..f4361767b 100644 --- a/middlewares/authenticator.go +++ b/middlewares/authenticator.go @@ -31,9 +31,13 @@ func NewAuthenticator(authConfig *types.Auth) (*Authenticator, error) { basicAuth := auth.NewBasicAuthenticator("traefik", authenticator.secretBasic) authenticator.handler = negroni.HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { if username := basicAuth.CheckAuth(r); username == "" { - log.Debugf("Auth failed...") + log.Debugf("Basic auth failed...") basicAuth.RequireAuth(w, r) } else { + log.Debugf("Basic auth success...") + if authConfig.HeaderField != "" { + r.Header[authConfig.HeaderField] = []string{username} + } next.ServeHTTP(w, r) } }) @@ -45,8 +49,13 @@ func NewAuthenticator(authConfig *types.Auth) (*Authenticator, error) { digestAuth := auth.NewDigestAuthenticator("traefik", authenticator.secretDigest) authenticator.handler = negroni.HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { if username, _ := digestAuth.CheckAuth(r); username == "" { + log.Debugf("Digest auth failed...") digestAuth.RequireAuth(w, r) } else { + log.Debugf("Digest auth success...") + if authConfig.HeaderField != "" { + r.Header[authConfig.HeaderField] = []string{username} + } next.ServeHTTP(w, r) } }) diff --git a/middlewares/authenticator_test.go b/middlewares/authenticator_test.go index 0d3eff016..c4c5ec645 100644 --- a/middlewares/authenticator_test.go +++ b/middlewares/authenticator_test.go @@ -101,3 +101,33 @@ func TestDigestAuthFail(t *testing.T) { assert.NoError(t, err, "there should be no error") assert.Equal(t, http.StatusUnauthorized, res.StatusCode, "they should be equal") } + +func TestBasicAuthUserHeader(t *testing.T) { + authMiddleware, err := NewAuthenticator(&types.Auth{ + Basic: &types.Basic{ + Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"}, + }, + HeaderField: "X-WebAuth-User", + }) + assert.NoError(t, err, "there should be no error") + + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "test", r.Header["X-WebAuth-User"][0], "auth user should be set") + fmt.Fprintln(w, "traefik") + }) + n := negroni.New(authMiddleware) + n.UseHandler(handler) + ts := httptest.NewServer(n) + defer ts.Close() + + client := &http.Client{} + req, err := http.NewRequest("GET", ts.URL, nil) + req.SetBasicAuth("test", "test") + res, err := client.Do(req) + assert.NoError(t, err, "there should be no error") + assert.Equal(t, http.StatusOK, res.StatusCode, "they should be equal") + + body, err := ioutil.ReadAll(res.Body) + assert.NoError(t, err, "there should be no error") + assert.Equal(t, "traefik\n", string(body), "they should be equal") +} diff --git a/types/types.go b/types/types.go index f889c5506..a3aa626ee 100644 --- a/types/types.go +++ b/types/types.go @@ -222,8 +222,9 @@ type Cluster struct { // Auth holds authentication configuration (BASIC, DIGEST, users) type Auth struct { - Basic *Basic - Digest *Digest + Basic *Basic + Digest *Digest + HeaderField string } // Users authentication users