package webmiddleware import ( "net/http" "net/url" "regexp" webutils "git.mstar.dev/mstar/goutils/http" "git.mstar.dev/mstar/goutils/other" ) const signatureRegexString = `keyId=([a-zA-Z0-9_\-\.:@/\?&=#%\+\[\]!$\(\)\*,;]+),headers="([a-z0-9-_\(\) ]+)",(?:algorithm="([a-z0-9-])+",)?signature="(.+)"` var publicPaths = []*regexp.Regexp{ regexp.MustCompile(`/\.well-known/.+^`), // regexp.MustCompile(``), } var signatureRegex = regexp.MustCompile(signatureRegexString) // Builder for the authorized fetch check middleware. // forNonGet determines whether the check should happen for non-GET requests. // forGet determines whether the check should also happen for GET requests. // forGet being true implicitly sets forNonGet true too. // Requests to the server actor and other, required public resources // will never be checked func BuildAuthorizedFetchCheck(forNonGet bool, forGet bool) webutils.HandlerBuilder { return func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { path := r.URL.Path // Check always open path first for _, re := range publicPaths { if re.Match([]byte(path)) { h.ServeHTTP(w, r) return } } // Not an always open path, check methods if r.Method == "GET" && !forGet { h.ServeHTTP(w, r) return } else if !forGet && !forNonGet { h.ServeHTTP(w, r) return } // TODO: Perform check here signatureHeader := r.Header.Get("Signature") if signatureHeader == "" { webutils.ProblemDetails( w, http.StatusUnauthorized, "/errors/invalid-auth-signature", "invalid authorization signature", other.IntoPointer("Missing Signature header for authorization"), nil, ) return } match := signatureRegex.FindStringSubmatch(signatureHeader) if len(match) <= 1 { webutils.ProblemDetails( w, http.StatusUnauthorized, "/errors/invalid-auth-signature", "invalid authorization signature", other.IntoPointer("Invalid signature header format"), map[string]any{ "used signature regex": signatureRegexString, "signature format": `keyId="",headers="",(optional: algorithm="", (defaults to rsa-sha256 if not set))signature="signed message"`, }, ) return } // fullMatch = match[0] rawKeyId := match[1] rawHeaders := match[2] rawAlgorithm := match[3] signature := match[4] _, err := url.Parse(rawKeyId) if err != nil { webutils.ProblemDetails( w, http.StatusUnauthorized, "/errors/invalid-auth-signature", "invalid authorization signature", other.IntoPointer("keyId must be a valid url"), nil, ) return } if rawAlgorithm == "" { rawAlgorithm = "hs2019" // w.Header().Add("X-Algorithm-Hint", "") } _ = rawHeaders _ = signature _ = rawAlgorithm panic("not implemented") }) } }