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
This commit is contained in:
parent
913a297e8d
commit
94bb7a1435
4 changed files with 59 additions and 3 deletions
|
@ -114,4 +114,20 @@ defaultEntryPoints = ["http"]
|
||||||
address = ":80"
|
address = ":80"
|
||||||
[entryPoints.http.auth.basic]
|
[entryPoints.http.auth.basic]
|
||||||
users = ["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"]
|
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"]
|
||||||
```
|
```
|
|
@ -31,9 +31,13 @@ func NewAuthenticator(authConfig *types.Auth) (*Authenticator, error) {
|
||||||
basicAuth := auth.NewBasicAuthenticator("traefik", authenticator.secretBasic)
|
basicAuth := auth.NewBasicAuthenticator("traefik", authenticator.secretBasic)
|
||||||
authenticator.handler = negroni.HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
authenticator.handler = negroni.HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||||
if username := basicAuth.CheckAuth(r); username == "" {
|
if username := basicAuth.CheckAuth(r); username == "" {
|
||||||
log.Debugf("Auth failed...")
|
log.Debugf("Basic auth failed...")
|
||||||
basicAuth.RequireAuth(w, r)
|
basicAuth.RequireAuth(w, r)
|
||||||
} else {
|
} else {
|
||||||
|
log.Debugf("Basic auth success...")
|
||||||
|
if authConfig.HeaderField != "" {
|
||||||
|
r.Header[authConfig.HeaderField] = []string{username}
|
||||||
|
}
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -45,8 +49,13 @@ func NewAuthenticator(authConfig *types.Auth) (*Authenticator, error) {
|
||||||
digestAuth := auth.NewDigestAuthenticator("traefik", authenticator.secretDigest)
|
digestAuth := auth.NewDigestAuthenticator("traefik", authenticator.secretDigest)
|
||||||
authenticator.handler = negroni.HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
authenticator.handler = negroni.HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||||
if username, _ := digestAuth.CheckAuth(r); username == "" {
|
if username, _ := digestAuth.CheckAuth(r); username == "" {
|
||||||
|
log.Debugf("Digest auth failed...")
|
||||||
digestAuth.RequireAuth(w, r)
|
digestAuth.RequireAuth(w, r)
|
||||||
} else {
|
} else {
|
||||||
|
log.Debugf("Digest auth success...")
|
||||||
|
if authConfig.HeaderField != "" {
|
||||||
|
r.Header[authConfig.HeaderField] = []string{username}
|
||||||
|
}
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -101,3 +101,33 @@ func TestDigestAuthFail(t *testing.T) {
|
||||||
assert.NoError(t, err, "there should be no error")
|
assert.NoError(t, err, "there should be no error")
|
||||||
assert.Equal(t, http.StatusUnauthorized, res.StatusCode, "they should be equal")
|
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")
|
||||||
|
}
|
||||||
|
|
|
@ -222,8 +222,9 @@ type Cluster struct {
|
||||||
|
|
||||||
// Auth holds authentication configuration (BASIC, DIGEST, users)
|
// Auth holds authentication configuration (BASIC, DIGEST, users)
|
||||||
type Auth struct {
|
type Auth struct {
|
||||||
Basic *Basic
|
Basic *Basic
|
||||||
Digest *Digest
|
Digest *Digest
|
||||||
|
HeaderField string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Users authentication users
|
// Users authentication users
|
||||||
|
|
Loading…
Reference in a new issue