package middleware import ( "net/http" "strings" "github.com/rs/zerolog/log" "github.com/yusing/godoxy/internal/auth" "github.com/yusing/godoxy/internal/route/rules" httputils "github.com/yusing/goutils/http" ) type Bypass []rules.RuleOn func (b Bypass) ShouldBypass(w http.ResponseWriter, r *http.Request) bool { for _, rule := range b { if rule.Check(w, r) { log.Debug().Str("rule_matched", rule.String()).Str("url", r.Host+r.URL.Path).Msg("bypassing request") return true } } return false } type checkBypass struct { name string bypass Bypass modReq RequestModifier modRes ResponseModifier // when request path matches any of these prefixes, bypass is not applied enforcedPathPrefixes []string } func (c *checkBypass) isEnforced(r *http.Request) bool { for _, prefix := range c.enforcedPathPrefixes { if strings.HasPrefix(r.URL.Path, prefix) { return true } } return false } func (c *checkBypass) before(w http.ResponseWriter, r *http.Request) (proceedNext bool) { if c.modReq == nil || (!c.isEnforced(r) && c.bypass.ShouldBypass(w, r)) { return true } log.Debug().Str("middleware", c.name).Str("url", r.Host+r.URL.Path).Msg("modifying request") return c.modReq.before(w, r) } func (c *checkBypass) modifyResponse(resp *http.Response) error { if c.modRes == nil || (!c.isEnforced(resp.Request) && c.bypass.ShouldBypass(httputils.ResponseAsRW(resp), resp.Request)) { return nil } log.Debug().Str("middleware", c.name).Str("url", resp.Request.Host+resp.Request.URL.Path).Msg("modifying response") return c.modRes.modifyResponse(resp) } func (m *Middleware) withCheckBypass() any { if len(m.Bypass) > 0 { modReq, _ := m.impl.(RequestModifier) modRes, _ := m.impl.(ResponseModifier) return &checkBypass{ name: m.Name(), bypass: m.Bypass, enforcedPathPrefixes: getEnforcedPathPrefixes(modReq, modRes), modReq: modReq, modRes: modRes, } } return m.impl } func getEnforcedPathPrefixes(modReq RequestModifier, modRes ResponseModifier) []string { if modReq == nil && modRes == nil { return nil } switch modReq.(type) { case *oidcMiddleware: return []string{auth.OIDCAuthBasePath} } return nil }