mirror of
https://github.com/tinyauthapp/tinyauth.git
synced 2026-09-05 22:03:33 +08:00
should be non-breaking now ;)
This commit is contained in:
@@ -345,6 +345,19 @@ func (controller *ProxyController) getHeader(c *gin.Context, header string) (str
|
|||||||
return val, strings.TrimSpace(val) != ""
|
return val, strings.TrimSpace(val) != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getRequestPath(uri string) (string, error) {
|
||||||
|
parsedURI, err := url.ParseRequestURI(uri)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if parsedURI.Path == "" {
|
||||||
|
return "/", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsedURI.Path, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (controller *ProxyController) useBrowserResponse(proxyCtx ProxyContext) bool {
|
func (controller *ProxyController) useBrowserResponse(proxyCtx ProxyContext) bool {
|
||||||
// If it's nginx we need non-browser response
|
// If it's nginx we need non-browser response
|
||||||
if proxyCtx.ProxyType == Nginx {
|
if proxyCtx.ProxyType == Nginx {
|
||||||
@@ -390,16 +403,11 @@ func (controller *ProxyController) getForwardAuthContext(c *gin.Context) (ProxyC
|
|||||||
return ProxyContext{}, errors.New("x-forwarded-uri not found")
|
return ProxyContext{}, errors.New("x-forwarded-uri not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedURI, err := url.ParseRequestURI(uri)
|
path, err := getRequestPath(uri)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ProxyContext{}, fmt.Errorf("invalid x-forwarded-uri: %w", err)
|
return ProxyContext{}, fmt.Errorf("invalid x-forwarded-uri: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if parsedURI.Path == "" {
|
|
||||||
parsedURI.Path = "/"
|
|
||||||
}
|
|
||||||
|
|
||||||
proto, ok := controller.getHeader(c, "x-forwarded-proto")
|
proto, ok := controller.getHeader(c, "x-forwarded-proto")
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -413,7 +421,7 @@ func (controller *ProxyController) getForwardAuthContext(c *gin.Context) (ProxyC
|
|||||||
return ProxyContext{
|
return ProxyContext{
|
||||||
Host: host,
|
Host: host,
|
||||||
Proto: proto,
|
Proto: proto,
|
||||||
Path: parsedURI.Path,
|
Path: path,
|
||||||
Method: method,
|
Method: method,
|
||||||
Type: ForwardAuth,
|
Type: ForwardAuth,
|
||||||
}, nil
|
}, nil
|
||||||
@@ -445,6 +453,9 @@ func (controller *ProxyController) getAuthRequestContext(c *gin.Context) (ProxyC
|
|||||||
}
|
}
|
||||||
|
|
||||||
path := url.Path
|
path := url.Path
|
||||||
|
if path == "" {
|
||||||
|
path = "/"
|
||||||
|
}
|
||||||
method := c.Request.Method
|
method := c.Request.Method
|
||||||
|
|
||||||
return ProxyContext{
|
return ProxyContext{
|
||||||
@@ -472,7 +483,10 @@ func (controller *ProxyController) getExtAuthzContext(c *gin.Context) (ProxyCont
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We get the path from the query string
|
// We get the path from the query string
|
||||||
path := c.Query("path")
|
path, err := getRequestPath(c.Query("path"))
|
||||||
|
if err != nil {
|
||||||
|
return ProxyContext{}, fmt.Errorf("invalid path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// For envoy we need to support every method
|
// For envoy we need to support every method
|
||||||
method := c.Request.Method
|
method := c.Request.Method
|
||||||
|
|||||||
@@ -347,6 +347,16 @@ func TestProxyController(t *testing.T) {
|
|||||||
assert.Equal(t, http.StatusOK, recorder.Code)
|
assert.Equal(t, http.StatusOK, recorder.Code)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: "Ensure path allow ACL ignores query strings for nginx auth request",
|
||||||
|
middlewares: []gin.HandlerFunc{},
|
||||||
|
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
|
||||||
|
req := httptest.NewRequest("GET", "/api/auth/nginx", nil)
|
||||||
|
req.Header.Set("x-original-url", "https://path-allow.example.com/admin?path=/allowed")
|
||||||
|
router.ServeHTTP(recorder, req)
|
||||||
|
assert.Equal(t, http.StatusUnauthorized, recorder.Code)
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
description: "Ensure path allow ACL works on nginx auth request",
|
description: "Ensure path allow ACL works on nginx auth request",
|
||||||
middlewares: []gin.HandlerFunc{},
|
middlewares: []gin.HandlerFunc{},
|
||||||
@@ -357,6 +367,17 @@ func TestProxyController(t *testing.T) {
|
|||||||
assert.Equal(t, http.StatusOK, recorder.Code)
|
assert.Equal(t, http.StatusOK, recorder.Code)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: "Ensure path allow ACL ignores query strings for envoy ext authz",
|
||||||
|
middlewares: []gin.HandlerFunc{},
|
||||||
|
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
|
||||||
|
req := httptest.NewRequest("HEAD", "/api/auth/envoy?path=/admin%3Fpath%3D/allowed", nil)
|
||||||
|
req.Host = "path-allow.example.com"
|
||||||
|
req.Header.Set("x-forwarded-proto", "https")
|
||||||
|
router.ServeHTTP(recorder, req)
|
||||||
|
assert.Equal(t, http.StatusUnauthorized, recorder.Code)
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
description: "Ensure path allow ACL works on envoy ext authz",
|
description: "Ensure path allow ACL works on envoy ext authz",
|
||||||
middlewares: []gin.HandlerFunc{},
|
middlewares: []gin.HandlerFunc{},
|
||||||
|
|||||||
@@ -182,32 +182,23 @@ type AuthEnabledRule struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func matchPathRule(paths, path string) (bool, error) {
|
func matchPathRule(paths, path string) (bool, error) {
|
||||||
|
paths = strings.TrimSpace(paths)
|
||||||
|
|
||||||
|
if paths == "/" {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(paths, "/") && strings.HasSuffix(paths, "/") {
|
||||||
|
regex, err := regexp.Compile(paths[1 : len(paths)-1])
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("invalid path regex %q: %w", paths, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return regex.MatchString(path), nil
|
||||||
|
}
|
||||||
|
|
||||||
for _, configuredPath := range strings.Split(paths, ",") {
|
for _, configuredPath := range strings.Split(paths, ",") {
|
||||||
configuredPath = strings.TrimSpace(configuredPath)
|
if strings.HasPrefix(path, strings.TrimSpace(configuredPath)) {
|
||||||
|
|
||||||
if configuredPath == "/" {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if configuredPath == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// only apply regex if the path starts and ends with a slash, e.g. /regex/
|
|
||||||
if strings.HasPrefix(configuredPath, "/") && strings.HasSuffix(configuredPath, "/") {
|
|
||||||
regex, err := regexp.Compile(configuredPath[1 : len(configuredPath)-1])
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("invalid path regex %q: %w", configuredPath, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if regex.MatchString(path) {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(path, configuredPath) {
|
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -536,6 +536,16 @@ func TestAuthEnabledRule(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: EffectAllow,
|
expected: EffectAllow,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "allows when path matches a comma-separated allow path",
|
||||||
|
ctx: &ACLContext{
|
||||||
|
ACLs: &model.App{
|
||||||
|
Path: model.AppPath{Allow: "/bar,/foo/bar,/hello"},
|
||||||
|
},
|
||||||
|
Path: "/foo/bar/baz",
|
||||||
|
},
|
||||||
|
expected: EffectAllow,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "allows when path matches allow regex",
|
name: "allows when path matches allow regex",
|
||||||
ctx: &ACLContext{
|
ctx: &ACLContext{
|
||||||
@@ -546,6 +556,16 @@ func TestAuthEnabledRule(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: EffectAllow,
|
expected: EffectAllow,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "denies when comma-separated allow paths do not match",
|
||||||
|
ctx: &ACLContext{
|
||||||
|
ACLs: &model.App{
|
||||||
|
Path: model.AppPath{Allow: "/bar,/foo/bar,/hello"},
|
||||||
|
},
|
||||||
|
Path: "/private",
|
||||||
|
},
|
||||||
|
expected: EffectDeny,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "denies when path does not match allow path",
|
name: "denies when path does not match allow path",
|
||||||
ctx: &ACLContext{
|
ctx: &ACLContext{
|
||||||
|
|||||||
Reference in New Issue
Block a user