Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions modules/caddyhttp/caddyauth/caddyauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ func init() {
// Authentication is a middleware which provides user authentication.
// Rejects requests with HTTP 401 if the request is not authenticated.
//
// After a successful authentication, the placeholder
// When an authentication provider returns user information, the placeholder
// `{http.auth.user.id}` will be set to the username, and also
// `{http.auth.user.*}` placeholders may be set for any authentication
// modules that provide user metadata.
// modules that provide user metadata, even if authentication is rejected.
//
// In case of an error, the placeholder `{http.auth.<provider>.error}`
// will be set to the error message returned by the authentication
Expand Down Expand Up @@ -91,6 +91,12 @@ func (a Authentication) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
repl.Set("http.auth."+provName+".error", err.Error())
continue
}
if authed || user.ID != "" || len(user.Metadata) > 0 {
repl.Set("http.auth.user.id", user.ID)
for k, v := range user.Metadata {
repl.Set("http.auth.user."+k, v)
}
}
if authed {
break
}
Expand All @@ -99,11 +105,6 @@ func (a Authentication) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
return caddyhttp.Error(http.StatusUnauthorized, fmt.Errorf("not authenticated"))
}

repl.Set("http.auth.user.id", user.ID)
for k, v := range user.Metadata {
repl.Set("http.auth.user."+k, v)
}

return next.ServeHTTP(w, r)
}

Expand Down
82 changes: 82 additions & 0 deletions modules/caddyhttp/caddyauth/caddyauth_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright 2015 Matthew Holt and The Caddy Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package caddyauth

import (
"context"
"errors"
"net/http"
"net/http/httptest"
"testing"

"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
)

func TestAuthenticationSetsUserPlaceholdersOnUnauthorized(t *testing.T) {
repl := caddy.NewReplacer()
req := httptest.NewRequest(http.MethodGet, "/", nil)
req = req.WithContext(context.WithValue(req.Context(), caddy.ReplacerCtxKey, repl))

a := Authentication{
Providers: map[string]Authenticator{
"test": staticAuthenticator{
user: User{
ID: "alice",
Metadata: map[string]string{
"role": "admin",
},
},
},
},
}

nextCalled := false
err := a.ServeHTTP(httptest.NewRecorder(), req, caddyhttp.HandlerFunc(func(http.ResponseWriter, *http.Request) error {
nextCalled = true
return nil
}))
if err == nil {
t.Fatal("expected unauthorized error")
}

var handlerErr caddyhttp.HandlerError
if !errors.As(err, &handlerErr) {
t.Fatalf("expected caddyhttp.HandlerError, got %T", err)
}
if handlerErr.StatusCode != http.StatusUnauthorized {
t.Fatalf("expected status %d, got %d", http.StatusUnauthorized, handlerErr.StatusCode)
}
if nextCalled {
t.Fatal("next handler was called")
}

if got, ok := repl.GetString("http.auth.user.id"); !ok || got != "alice" {
t.Fatalf("expected http.auth.user.id to be alice, got %q (ok=%v)", got, ok)
}
if got, ok := repl.GetString("http.auth.user.role"); !ok || got != "admin" {
t.Fatalf("expected http.auth.user.role to be admin, got %q (ok=%v)", got, ok)
}
}

type staticAuthenticator struct {
user User
authed bool
err error
}

func (s staticAuthenticator) Authenticate(http.ResponseWriter, *http.Request) (User, bool, error) {
return s.user, s.authed, s.err
}
Loading