diff --git a/README.md b/README.md index 4af3735..0d6820b 100644 --- a/README.md +++ b/README.md @@ -150,6 +150,14 @@ You have several ways to provide parameters to your webhook script: Therefore the name can be altered. *ex: `CONTENT-TYPE` will become `content_type`.* +Webhookd adds some additional parameters to the script: + +- `hook_id`: hook ID (auto-increment) +- `hook_name`: hook name +- `hook_method`: HTTP request method +- `x_forwarded_for`: client IP +- `x_webauth_user`: username if authentication is enabled + *Example:* The script: diff --git a/pkg/auth/authenticator.go b/pkg/auth/authenticator.go index 98b0df3..ccad39d 100644 --- a/pkg/auth/authenticator.go +++ b/pkg/auth/authenticator.go @@ -4,7 +4,8 @@ import ( "net/http" ) -// Authenticator is a generic interface to validate an HTTP request +// Authenticator is a generic interface to validate HTTP request credentials. +// It's returns the authentication result along with the principal (username) if it has one. type Authenticator interface { - Validate(r *http.Request) bool + Validate(r *http.Request) (bool, *string) } diff --git a/pkg/auth/htpasswd-file.go b/pkg/auth/htpasswd-file.go index 4ed9515..017832b 100644 --- a/pkg/auth/htpasswd-file.go +++ b/pkg/auth/htpasswd-file.go @@ -52,12 +52,12 @@ func NewHtpasswdFromFile(path string) (*HtpasswdFile, error) { } // Validate HTTP request credentials -func (h *HtpasswdFile) Validate(r *http.Request) bool { +func (h *HtpasswdFile) Validate(r *http.Request) (bool, *string) { user, passwd, ok := r.BasicAuth() - if !ok { - return false + if ok && h.validateCredentials(user, passwd) { + return true, &user } - return h.validateCredentials(user, passwd) + return false, nil } func (h *HtpasswdFile) validateCredentials(user string, password string) bool { diff --git a/pkg/auth/test/htpasswd-file_test.go b/pkg/auth/test/htpasswd-file_test.go index 07e35f8..56444e9 100644 --- a/pkg/auth/test/htpasswd-file_test.go +++ b/pkg/auth/test/htpasswd-file_test.go @@ -16,8 +16,12 @@ func TestValidateCredentials(t *testing.T) { req, err := http.NewRequest("POST", "http://localhost:8080", nil) assert.Nil(t, err, "") req.SetBasicAuth("foo", "bar") - assert.Equal(t, true, htpasswdFile.Validate(req), "credentials should be valid") + ok, username := htpasswdFile.Validate(req) + assert.Equal(t, true, ok, "credentials should be valid") + assert.Equal(t, "foo", *username, "invalid username") req.SetBasicAuth("foo", "bad") - assert.Equal(t, false, htpasswdFile.Validate(req), "credentials should not be valid") + ok, username = htpasswdFile.Validate(req) + assert.Equal(t, false, ok, "credentials should be invalid") + assert.True(t, username == nil, "username should be nil") } diff --git a/pkg/middleware/authn.go b/pkg/middleware/authn.go index 7933800..a55e809 100644 --- a/pkg/middleware/authn.go +++ b/pkg/middleware/authn.go @@ -6,11 +6,17 @@ import ( "github.com/ncarlier/webhookd/pkg/auth" ) +const xWebAuthUser = "X-WebAuth-User" + // AuthN is a middleware to checks HTTP request credentials func AuthN(authenticator auth.Authenticator) Middleware { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if authenticator.Validate(r) { + w.Header().Del(xWebAuthUser) + if ok, username := authenticator.Validate(r); ok { + if username != nil { + w.Header().Set(xWebAuthUser, *username) + } next.ServeHTTP(w, r) return }