104 lines
3 KiB
Go
104 lines
3 KiB
Go
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="<url to actor with used key>",headers="<list of headers used, including pseudo header (request-target)>",(optional: algorithm="<one of rsa-sha256, hs2019>", (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")
|
|
})
|
|
}
|
|
}
|