labstack / echo

High performance, minimalist Go web framework
https://echo.labstack.com
MIT License
29.94k stars 2.23k forks source link

How to check if a route is supported by echo router #2693

Closed prudhviraj-nammi-ev closed 3 weeks ago

prudhviraj-nammi-ev commented 3 weeks ago

Issue Description

Im writing a middleware which checks if user has admin role and throws a 403 status if he doesn't. Since the middleware is getting called before handler , Im getting 403 status even for paths which do not exists. Is there a way using which I can identify if the request route is valid or not?

Checklist

Expected behaviour

Need a way to check if requested route is valid.

Actual behaviour

No way to check if requested route is valid.

Steps to reproduce

Create a middleware which returns 403 status based on some validations. Call invalid route, you will get 403 instead of 404.

Working code to debug

package main

func RoleHandler(User user.IUser) echo.MiddlewareFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(context echo.Context) error {
            requestUri := context.Request().RequestURI
                if !User.IsAdminUser() {
                    return context.JSON(http.StatusForbidden, nil)
                }
            }
            return next(context)
        }
    }
}

Version/commit

aldas commented 3 weeks ago

Are you adding middlewares with e.Pre() or with e.Use()? or this is group middleware?

This is small example for differently added middlewares behave

  1. This is output for existing group level route curl http://localhost:8080/group/test

    2024/10/30 17:18:00 INFO e.Pre URI=/group/test Path=""
    2024/10/30 17:18:00 INFO e.Use URI=/group/test Path=/group/test
    2024/10/30 17:18:00 INFO g.Group URI=/group/test Path=/group/test
    2024/10/30 17:18:00 INFO g.Use URI=/group/test Path=/group/test
  2. This is output for request that does not match any routes curl http://localhost:8080/not-existing

    2024/10/30 17:18:02 INFO e.Pre URI=/not-existing Path=""
    2024/10/30 17:18:02 INFO e.Use URI=/not-existing Path=""
  3. Output for group level not existing route curl http://localhost:8080/group/not-existing

    2024/10/30 17:18:04 INFO e.Pre URI=/group/not-existing Path=""
    2024/10/30 17:18:04 INFO e.Use URI=/group/not-existing Path=/group/*
    2024/10/30 17:18:04 INFO g.Group URI=/group/not-existing Path=/group/*
    2024/10/30 17:18:04 INFO g.Use URI=/group/not-existing Path=/group/*

    NB: g.Group and g.Use are executed because group level middleware are added also as 404 handlers to that group.

package main

import (
    "fmt"
    "github.com/labstack/echo/v4"
    "log/slog"
    "net/http"
)

func createMw(where string) func(next echo.HandlerFunc) echo.HandlerFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            slog.Info(where, slog.String("URI", c.Request().RequestURI), slog.String("Path", c.Path()))
            return next(c)
        }
    }
}
func main() {
    e := echo.New()

    e.Pre(createMw("e.Pre"))
    e.Use(createMw("e.Use"))

    g := e.Group("/group", createMw("g.Group"))
    g.Use(createMw("g.Use"))

    g.GET("/test", func(c echo.Context) error {
        return c.String(http.StatusOK, "OK")
    })

    if err := e.Start(":8080"); err != nil && err != http.ErrServerClosed {
        slog.Error("err: %s", slog.Any("error", err))
    }
}

so if you are not dealing with groups you could check c.Path() == "" for no match, but when it comes to group level routes - on top of my head - I think you can not determine no match.

aldas commented 3 weeks ago

depending on security philosophical standpoint you could say, at some situations, that requester should not be able to determine if route exists or not.

prudhviraj-nammi-ev commented 3 weeks ago

@aldas Im using e.Use() without groups. But c.Path() is returning path even if there is a mismatch in http methods. Is this expected?

aldas commented 3 weeks ago

in case of e.Use() try checking c.Path() == "". Path is filled by router when it matched something. And if I recall correctly - empty route ala e.GET("",... would have / has Path value.

prudhviraj-nammi-ev commented 3 weeks ago

But c.Path() is returning path even if there is a mismatch in http methods with route match. Is this expected?

aldas commented 3 weeks ago

yep, for 405 Path gets filled. Path being empty or not, is actually accidental feature. Middlewares are not suppose to know if they lead to actual route or not - well at current state of Echo.

are you dealing with OPTIONS (cors) requests being checked for auth? In that case you could move CORS mw before auth middleware.

prudhviraj-nammi-ev commented 3 weeks ago

Thanks, no we are currently not dealing with cors.