AuthProxy: Support Regexps when checking if path requires auth

Change-Id: I3bc6d3143cef46b711bb3ccc7e2cb1ee8c59313f
diff --git a/charts/auth-proxy/templates/install.yaml b/charts/auth-proxy/templates/install.yaml
index 2ce79bc..9843d60 100644
--- a/charts/auth-proxy/templates/install.yaml
+++ b/charts/auth-proxy/templates/install.yaml
@@ -45,4 +45,4 @@
         - --membership-public-addr={{ .Values.membershipPublicAddr }}
         - --groups={{ .Values.groups }}
         - --upstream={{ .Values.upstream }}
-        - --no-auth-path-prefixes={{ .Values.noAuthPathPrefixes }}
+        - --no-auth-path-patterns={{ .Values.noAuthPathPatterns }}
diff --git a/charts/auth-proxy/values.yaml b/charts/auth-proxy/values.yaml
index 45bc602..df26e50 100644
--- a/charts/auth-proxy/values.yaml
+++ b/charts/auth-proxy/values.yaml
@@ -10,4 +10,4 @@
 membershipPublicAddr: https://memberships.p.example.com
 groups: ""
 portName: http
-noAuthPathPrefixes: ""
+noAuthPathPatterns: ""
diff --git a/core/auth/proxy/main.go b/core/auth/proxy/main.go
index a147049..f8ab620 100644
--- a/core/auth/proxy/main.go
+++ b/core/auth/proxy/main.go
@@ -14,6 +14,7 @@
 	"net/http"
 	"net/http/cookiejar"
 	"net/url"
+	"regexp"
 	"slices"
 	"strings"
 )
@@ -25,7 +26,7 @@
 var membershipPublicAddr = flag.String("membership-public-addr", "", "Public address of membership service")
 var groups = flag.String("groups", "", "Comma separated list of groups. User must be part of at least one of them. If empty group membership will not be checked.")
 var upstream = flag.String("upstream", "", "Upstream service address")
-var noAuthPathPrefixes = flag.String("no-auth-path-prefixes", "", "Path prefixes to disable authentication for")
+var noAuthPathPatterns = flag.String("no-auth-path-patterns", "", "Path regex patterns to disable authentication for")
 
 //go:embed unauthorized.html
 var unauthorizedHTML embed.FS
@@ -33,6 +34,23 @@
 //go:embed static/*
 var f embed.FS
 
+var noAuthPathRegexps []*regexp.Regexp
+
+func initPathPatterns() error {
+	for _, p := range strings.Split(*noAuthPathPatterns, ",") {
+		t := strings.TrimSpace(p)
+		if len(t) == 0 {
+			continue
+		}
+		exp, err := regexp.Compile(t)
+		if err != nil {
+			return err
+		}
+		noAuthPathRegexps = append(noAuthPathRegexps, exp)
+	}
+	return nil
+}
+
 type cachingHandler struct {
 	h http.Handler
 }
@@ -100,9 +118,8 @@
 		return
 	}
 	reqAuth := true
-	for _, p := range strings.Split(*noAuthPathPrefixes, ",") {
-		t := strings.TrimSpace(p)
-		if len(t) > 0 && strings.HasPrefix(r.URL.Path, t) {
+	for _, p := range noAuthPathRegexps {
+		if p.MatchString(r.URL.Path) {
 			reqAuth = false
 			break
 		}
@@ -255,6 +272,9 @@
 	if *groups != "" && (*membershipAddr == "" || *membershipPublicAddr == "") {
 		log.Fatal("membership-addr and membership-public-addr flags are required when groups are provided")
 	}
+	if err := initPathPatterns(); err != nil {
+		log.Fatal(err)
+	}
 	http.Handle("/.auth/static/", http.StripPrefix("/.auth", cachingHandler{http.FileServer(http.FS(f))}))
 	http.HandleFunc("/", handle)
 	fmt.Printf("Starting HTTP server on port: %d\n", *port)
diff --git a/core/installer/app_configs/app_base.cue b/core/installer/app_configs/app_base.cue
index 4075281..74860fa 100644
--- a/core/installer/app_configs/app_base.cue
+++ b/core/installer/app_configs/app_base.cue
@@ -38,7 +38,7 @@
 #Auth: {
 	enabled: bool | *false // TODO(gio): enabled by default?
 	groups: string | *"" // TODO(gio): []string
-	noAuthPathPrefixes: [...string] | *[]
+	noAuthPathPatterns: [...string] | *[]
 }
 
 #Image: {
diff --git a/core/installer/app_configs/app_global_env.cue b/core/installer/app_configs/app_global_env.cue
index fba9dcf..32cc63d 100644
--- a/core/installer/app_configs/app_global_env.cue
+++ b/core/installer/app_configs/app_global_env.cue
@@ -114,7 +114,7 @@
 						membershipPublicAddr: "https://memberships.\(g.privateDomain)"
 					}
 					groups: auth.groups
-					noAuthPathPrefixes: strings.Join(auth.noAuthPathPrefixes, ",")
+					noAuthPathPatterns: strings.Join(auth.noAuthPathPatterns, ",")
 					portName: _authProxyHTTPPortName
 				}
 			}
diff --git a/core/installer/server/dodo-app/static/schemas/app.schema.json b/core/installer/server/dodo-app/static/schemas/app.schema.json
index 6e49d62..5450358 100644
--- a/core/installer/server/dodo-app/static/schemas/app.schema.json
+++ b/core/installer/server/dodo-app/static/schemas/app.schema.json
@@ -236,12 +236,9 @@
                 "groups": {
                   "type": "string"
                 },
-                "noAuthPathPrefixes": {
+                "noAuthPathPatterns": {
                   "type": "array",
-                  "items": {
-                    "type": "string",
-					"pattern": "^/.*"
-                  }
+                  "items": { "type": "string" }
                 }
               },
               "additionalProperties": false,
diff --git a/core/installer/values-tmpl/dodo-app.cue b/core/installer/values-tmpl/dodo-app.cue
index cb61b45..1c5939b 100644
--- a/core/installer/values-tmpl/dodo-app.cue
+++ b/core/installer/values-tmpl/dodo-app.cue
@@ -105,10 +105,10 @@
 				}
 				if !input.external {
 					enabled: true
-					noAuthPathPrefixes: [
-						"/static/",
-						"/schemas/",
-						"/api/public-data",
+					noAuthPathPatterns: [
+						"^/static/.*$",
+						"^/schemas/.*$",
+						"^/api/public-data$",
 				    ]
 				}
 			}