mirror of
https://github.com/restic/rest-server.git
synced 2026-10-15 15:10:07 -07:00
auth: include Basic challenge in 401 responses
Requests rejected by Basic authentication currently return 401 without WWW-Authenticate, preventing HTTP clients from prompting for credentials. Advertise the Basic scheme on the server and metrics auth paths while leaving proxy authentication unchanged.
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
Bugfix: Include a Basic authentication challenge when credentials are rejected
|
||||||
|
|
||||||
|
Requests rejected by Basic authentication now include a `WWW-Authenticate` header,
|
||||||
|
allowing HTTP clients to prompt for credentials.
|
||||||
|
|
||||||
|
https://github.com/restic/rest-server/issues/376
|
||||||
+1
-1
@@ -59,7 +59,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
// First of all, check auth (will always pass if NoAuth is set)
|
// First of all, check auth (will always pass if NoAuth is set)
|
||||||
username, ok := s.checkAuth(r)
|
username, ok := s.checkAuth(r)
|
||||||
if !ok {
|
if !ok {
|
||||||
httpDefaultError(w, http.StatusUnauthorized)
|
s.writeUnauthorized(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,11 +56,18 @@ func (s *Server) checkAuth(r *http.Request) (username string, ok bool) {
|
|||||||
return username, true
|
return username, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) writeUnauthorized(w http.ResponseWriter) {
|
||||||
|
if !s.NoAuth && s.ProxyAuthUsername == "" {
|
||||||
|
w.Header().Set("WWW-Authenticate", `Basic realm="rest-server"`)
|
||||||
|
}
|
||||||
|
httpDefaultError(w, http.StatusUnauthorized)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) wrapMetricsAuth(f http.HandlerFunc) http.HandlerFunc {
|
func (s *Server) wrapMetricsAuth(f http.HandlerFunc) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
username, ok := s.checkAuth(r)
|
username, ok := s.checkAuth(r)
|
||||||
if !ok {
|
if !ok {
|
||||||
httpDefaultError(w, http.StatusUnauthorized)
|
s.writeUnauthorized(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if s.PrivateRepos && username != "metrics" {
|
if s.PrivateRepos && username != "metrics" {
|
||||||
|
|||||||
+55
@@ -1,6 +1,7 @@
|
|||||||
package restserver
|
package restserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@@ -78,3 +79,57 @@ func TestCheckAuth(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUnauthorizedResponsesIncludeBasicChallenge(t *testing.T) {
|
||||||
|
const challenge = `Basic realm="rest-server"`
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
server *Server
|
||||||
|
metrics bool
|
||||||
|
wantChallenge string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "server Basic authentication",
|
||||||
|
server: &Server{},
|
||||||
|
wantChallenge: challenge,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "server proxy authentication",
|
||||||
|
server: &Server{ProxyAuthUsername: "X-Remote-User"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "metrics Basic authentication",
|
||||||
|
server: &Server{},
|
||||||
|
metrics: true,
|
||||||
|
wantChallenge: challenge,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "metrics proxy authentication",
|
||||||
|
server: &Server{ProxyAuthUsername: "X-Remote-User"},
|
||||||
|
metrics: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
var handler http.HandlerFunc
|
||||||
|
if tt.metrics {
|
||||||
|
handler = tt.server.wrapMetricsAuth(func(http.ResponseWriter, *http.Request) {
|
||||||
|
t.Fatal("handler should not be called for an unauthorized request")
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
handler = tt.server.ServeHTTP
|
||||||
|
}
|
||||||
|
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
handler(rec, httptest.NewRequest(http.MethodGet, "/", nil))
|
||||||
|
if rec.Code != http.StatusUnauthorized {
|
||||||
|
t.Fatalf("status = %d, want %d", rec.Code, http.StatusUnauthorized)
|
||||||
|
}
|
||||||
|
if got := rec.Header().Get("WWW-Authenticate"); got != tt.wantChallenge {
|
||||||
|
t.Errorf("WWW-Authenticate = %q, want %q", got, tt.wantChallenge)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user