fix(server): respond with 404/405 appropriately (#3087)
This adjusts the not found handler to not respond with a 404 on not found endpoints that are part of the /api or /.well-known folders, and respond with a 405 when the method isn't implemented. Co-authored-by: Amir Zarrinkafsh <nightah@me.com>pull/3108/head^2
parent
fa143ea029
commit
2502d89682
|
@ -190,3 +190,10 @@ func respondUnauthorized(ctx *middlewares.AutheliaCtx, message string) {
|
|||
ctx.SetStatusCode(fasthttp.StatusUnauthorized)
|
||||
ctx.SetJSONError(message)
|
||||
}
|
||||
|
||||
// SetStatusCodeResponse writes a response status code and an appropriate body on either a
|
||||
// *fasthttp.RequestCtx or *middlewares.AutheliaCtx.
|
||||
func SetStatusCodeResponse(ctx responseWriter, statusCode int) {
|
||||
ctx.SetStatusCode(statusCode)
|
||||
ctx.SetBodyString(fmt.Sprintf("%d %s", statusCode, fasthttp.StatusMessage(statusCode)))
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/authentication"
|
||||
)
|
||||
|
||||
|
@ -124,3 +126,12 @@ type PassworPolicyBody struct {
|
|||
RequireNumber bool `json:"require_number"`
|
||||
RequireSpecial bool `json:"require_special"`
|
||||
}
|
||||
|
||||
type responseWriter interface {
|
||||
SetStatusCode(statusCode int)
|
||||
SetBodyString(body string)
|
||||
SetBody(body []byte)
|
||||
SetContentType(contentType string)
|
||||
SetContentTypeBytes(contentType []byte)
|
||||
SetBodyStream(bodyStream io.Reader, bodySize int)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,37 @@ const (
|
|||
logoFile = "logo.png"
|
||||
)
|
||||
|
||||
var rootFiles = []string{"favicon.ico", "manifest.json", "robots.txt"}
|
||||
var (
|
||||
rootFiles = []string{"favicon.ico", "manifest.json", "robots.txt"}
|
||||
swaggerFiles = []string{
|
||||
"favicon-16x16.png",
|
||||
"favicon-32x32.png",
|
||||
"index.css",
|
||||
"oauth2-redirect.html",
|
||||
"swagger-initializer.js",
|
||||
"swagger-ui-bundle.js",
|
||||
"swagger-ui-bundle.js.map",
|
||||
"swagger-ui-es-bundle-core.js",
|
||||
"swagger-ui-es-bundle-core.js.map",
|
||||
"swagger-ui-es-bundle.js",
|
||||
"swagger-ui-es-bundle.js.map",
|
||||
"swagger-ui-standalone-preset.js",
|
||||
"swagger-ui-standalone-preset.js.map",
|
||||
"swagger-ui.css",
|
||||
"swagger-ui.css.map",
|
||||
"swagger-ui.js",
|
||||
"swagger-ui.js.map",
|
||||
}
|
||||
|
||||
// Directories excluded from the not found handler proceeding to the next() handler.
|
||||
httpServerDirs = []struct {
|
||||
name, prefix string
|
||||
}{
|
||||
{name: "/api", prefix: "/api/"},
|
||||
{name: "/.well-known", prefix: "/.well-known/"},
|
||||
{name: "/static", prefix: "/static/"},
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
dev = "dev"
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
|
||||
"github.com/authelia/authelia/v4/internal/handlers"
|
||||
)
|
||||
|
||||
func handleNotFound(next fasthttp.RequestHandler) fasthttp.RequestHandler {
|
||||
return func(ctx *fasthttp.RequestCtx) {
|
||||
path := strings.ToLower(string(ctx.Path()))
|
||||
|
||||
for i := 0; i < len(httpServerDirs); i++ {
|
||||
if path == httpServerDirs[i].name || strings.HasPrefix(path, httpServerDirs[i].prefix) {
|
||||
handlers.SetStatusCodeResponse(ctx, fasthttp.StatusNotFound)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
next(ctx)
|
||||
}
|
||||
}
|
|
@ -49,15 +49,18 @@ func registerRoutes(configuration schema.Configuration, providers middlewares.Pr
|
|||
r.GET("/", autheliaMiddleware(serveIndexHandler))
|
||||
r.OPTIONS("/", autheliaMiddleware(handleOPTIONS))
|
||||
|
||||
r.GET("/api/", autheliaMiddleware(serveSwaggerHandler))
|
||||
r.GET("/api/"+apiFile, autheliaMiddleware(serveSwaggerAPIHandler))
|
||||
|
||||
for _, f := range rootFiles {
|
||||
r.GET("/"+f, middlewares.AssetOverrideMiddleware(configuration.Server.AssetPath, embeddedFS))
|
||||
}
|
||||
|
||||
r.GET("/api/", autheliaMiddleware(serveSwaggerHandler))
|
||||
r.GET("/api/"+apiFile, autheliaMiddleware(serveSwaggerAPIHandler))
|
||||
|
||||
for _, file := range swaggerFiles {
|
||||
r.GET("/api/"+file, embeddedFS)
|
||||
}
|
||||
|
||||
r.GET("/static/{filepath:*}", middlewares.AssetOverrideMiddleware(configuration.Server.AssetPath, embeddedFS))
|
||||
r.ANY("/api/{filepath:*}", embeddedFS)
|
||||
|
||||
r.GET("/api/health", autheliaMiddleware(handlers.HealthGet))
|
||||
r.GET("/api/state", autheliaMiddleware(handlers.StateGet))
|
||||
|
@ -155,7 +158,12 @@ func registerRoutes(configuration schema.Configuration, providers middlewares.Pr
|
|||
r.GET("/debug/vars", expvarhandler.ExpvarHandler)
|
||||
}
|
||||
|
||||
r.NotFound = autheliaMiddleware(serveIndexHandler)
|
||||
r.NotFound = handleNotFound(autheliaMiddleware(serveIndexHandler))
|
||||
|
||||
r.HandleMethodNotAllowed = true
|
||||
r.MethodNotAllowed = func(ctx *fasthttp.RequestCtx) {
|
||||
handlers.SetStatusCodeResponse(ctx, fasthttp.StatusMethodNotAllowed)
|
||||
}
|
||||
|
||||
handler := middlewares.LogRequestMiddleware(r.Handler)
|
||||
if configuration.Server.Path != "" {
|
||||
|
|
|
@ -64,6 +64,10 @@ func (s *BackendProtectionScenario) TestInvalidEndpointsReturn404() {
|
|||
s.AssertRequestStatusCode("POST", fmt.Sprintf("%s/api/not_existing/second", AutheliaBaseURL), 404)
|
||||
}
|
||||
|
||||
func (s *BackendProtectionScenario) TestInvalidEndpointsReturn405() {
|
||||
s.AssertRequestStatusCode("PUT", fmt.Sprintf("%s/api/configuration", AutheliaBaseURL), 405)
|
||||
}
|
||||
|
||||
func TestRunBackendProtection(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping suite test in short mode")
|
||||
|
|
Loading…
Reference in New Issue