Serve pre-defined files in "public", add "security.txt", add CORS header for ".well-known" (#25974)

Replace #25892

Close  #21942
Close  #25464

Major changes:

1. Serve "robots.txt" and ".well-known/security.txt" in the "public"
custom path
* All files in "public/.well-known" can be served, just like
"public/assets"
3. Add a test for ".well-known/security.txt"
4. Simplify the "FileHandlerFunc" logic, now the paths are consistent so
the code can be simpler
5. Add CORS header for ".well-known" endpoints
6. Add logs to tell users they should move some of their legacy custom
public files

```
2023/07/19 13:00:37 cmd/web.go:178:serveInstalled() [E] Found legacy public asset "img" in CustomPath. Please move it to /work/gitea/custom/public/assets/img
2023/07/19 13:00:37 cmd/web.go:182:serveInstalled() [E] Found legacy public asset "robots.txt" in CustomPath. Please move it to /work/gitea/custom/public/robots.txt
```
This PR is not breaking.

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Giteabot <teabot@gitea.io>
This commit is contained in:
wxiaoguang 2023-07-21 20:14:20 +08:00 committed by GitHub
parent 2f0e79e639
commit 52fb936773
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 50 additions and 40 deletions

View file

@ -28,27 +28,15 @@ func AssetFS() *assetfs.LayeredFS {
return assetfs.Layered(CustomAssets(), BuiltinAssets())
}
// AssetsHandlerFunc implements the static handler for serving custom or original assets.
func AssetsHandlerFunc(prefix string) http.HandlerFunc {
// FileHandlerFunc implements the static handler for serving files in "public" assets
func FileHandlerFunc() http.HandlerFunc {
assetFS := AssetFS()
prefix = strings.TrimSuffix(prefix, "/") + "/"
return func(resp http.ResponseWriter, req *http.Request) {
subPath := req.URL.Path
if !strings.HasPrefix(subPath, prefix) {
return
}
subPath = strings.TrimPrefix(subPath, prefix)
if req.Method != "GET" && req.Method != "HEAD" {
resp.WriteHeader(http.StatusNotFound)
return
}
if handleRequest(resp, req, assetFS, subPath) {
return
}
resp.WriteHeader(http.StatusNotFound)
handleRequest(resp, req, assetFS, req.URL.Path)
}
}
@ -71,16 +59,17 @@ func setWellKnownContentType(w http.ResponseWriter, file string) {
}
}
func handleRequest(w http.ResponseWriter, req *http.Request, fs http.FileSystem, file string) bool {
func handleRequest(w http.ResponseWriter, req *http.Request, fs http.FileSystem, file string) {
// actually, fs (http.FileSystem) is designed to be a safe interface, relative paths won't bypass its parent directory, it's also fine to do a clean here
f, err := fs.Open(util.PathJoinRelX("assets", file))
f, err := fs.Open(util.PathJoinRelX(file))
if err != nil {
if os.IsNotExist(err) {
return false
w.WriteHeader(http.StatusNotFound)
return
}
w.WriteHeader(http.StatusInternalServerError)
log.Error("[Static] Open %q failed: %v", file, err)
return true
return
}
defer f.Close()
@ -88,17 +77,16 @@ func handleRequest(w http.ResponseWriter, req *http.Request, fs http.FileSystem,
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
log.Error("[Static] %q exists, but fails to open: %v", file, err)
return true
return
}
// Try to serve index file
// need to serve index file? (no at the moment)
if fi.IsDir() {
w.WriteHeader(http.StatusNotFound)
return true
return
}
serveContent(w, req, fi, fi.ModTime(), f)
return true
}
type GzipBytesProvider interface {

View file

@ -349,9 +349,4 @@ func loadServerFrom(rootCfg ConfigProvider) {
default:
LandingPageURL = LandingPage(landingPage)
}
HasRobotsTxt, err = util.IsFile(path.Join(CustomPath, "robots.txt"))
if err != nil {
log.Error("Unable to check if %s is a file. Error: %v", path.Join(CustomPath, "robots.txt"), err)
}
}