0
Fork 0
mirror of https://github.com/caddyserver/caddy.git synced 2024-12-30 22:34:15 -05:00

fileserver: Browse can show symlink target if enabled (#5973)

* Added optional subdirective to browse allowing to reveal symlink paths.

* Update modules/caddyhttp/fileserver/browsetplcontext.go

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
This commit is contained in:
Aziz Rmadi 2024-02-05 22:31:26 -06:00 committed by GitHub
parent a7479302fc
commit feb07a7b59
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 50 additions and 18 deletions

View file

@ -50,6 +50,8 @@ var BrowseTemplate string
type Browse struct { type Browse struct {
// Filename of the template to use instead of the embedded browse template. // Filename of the template to use instead of the embedded browse template.
TemplateFile string `json:"template_file,omitempty"` TemplateFile string `json:"template_file,omitempty"`
// Determines whether or not targets of symlinks should be revealed.
RevealSymlinks bool `json:"reveal_symlinks,omitempty"`
} }
func (fsrv *FileServer) serveBrowse(fileSystem fs.FS, root, dirPath string, w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error { func (fsrv *FileServer) serveBrowse(fileSystem fs.FS, root, dirPath string, w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {

View file

@ -962,7 +962,15 @@ footer {
<td> <td>
<a href="{{html .URL}}"> <a href="{{html .URL}}">
{{template "icon" .}} {{template "icon" .}}
{{- if not .SymlinkPath}}
<span class="name">{{html .Name}}</span> <span class="name">{{html .Name}}</span>
{{- else}}
<span class="name">{{html .Name}} <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-arrow-narrow-right" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M5 12l14 0" />
<path d="M15 16l4 -4" />
<path d="M15 8l4 4" />
</svg> {{html .SymlinkPath}}</span>
{{- end}}
</a> </a>
</td> </td>
{{- if .IsDir}} {{- if .IsDir}}

View file

@ -20,6 +20,7 @@ import (
"net/url" "net/url"
"os" "os"
"path" "path"
"path/filepath"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@ -74,12 +75,21 @@ func (fsrv *FileServer) directoryListing(ctx context.Context, fileSystem fs.FS,
size := info.Size() size := info.Size()
fileIsSymlink := isSymlink(info) fileIsSymlink := isSymlink(info)
symlinkPath := ""
if fileIsSymlink { if fileIsSymlink {
path := caddyhttp.SanitizedPathJoin(root, path.Join(urlPath, info.Name())) path := caddyhttp.SanitizedPathJoin(root, path.Join(urlPath, info.Name()))
fileInfo, err := fs.Stat(fileSystem, path) fileInfo, err := fs.Stat(fileSystem, path)
if err == nil { if err == nil {
size = fileInfo.Size() size = fileInfo.Size()
} }
if fsrv.Browse.RevealSymlinks {
symLinkTarget, err := filepath.EvalSymlinks(path)
if err == nil {
symlinkPath = symLinkTarget
}
}
// An error most likely means the symlink target doesn't exist, // An error most likely means the symlink target doesn't exist,
// which isn't entirely unusual and shouldn't fail the listing. // which isn't entirely unusual and shouldn't fail the listing.
// In this case, just use the size of the symlink itself, which // In this case, just use the size of the symlink itself, which
@ -93,14 +103,15 @@ func (fsrv *FileServer) directoryListing(ctx context.Context, fileSystem fs.FS,
u := url.URL{Path: "./" + name} // prepend with "./" to fix paths with ':' in the name u := url.URL{Path: "./" + name} // prepend with "./" to fix paths with ':' in the name
tplCtx.Items = append(tplCtx.Items, fileInfo{ tplCtx.Items = append(tplCtx.Items, fileInfo{
IsDir: isDir, IsDir: isDir,
IsSymlink: fileIsSymlink, IsSymlink: fileIsSymlink,
Name: name, Name: name,
Size: size, Size: size,
URL: u.String(), URL: u.String(),
ModTime: info.ModTime().UTC(), ModTime: info.ModTime().UTC(),
Mode: info.Mode(), Mode: info.Mode(),
Tpl: tplCtx, // a reference up to the template context is useful Tpl: tplCtx, // a reference up to the template context is useful
SymlinkPath: symlinkPath,
}) })
} }
@ -230,13 +241,14 @@ type crumb struct {
// fileInfo contains serializable information // fileInfo contains serializable information
// about a file or directory. // about a file or directory.
type fileInfo struct { type fileInfo struct {
Name string `json:"name"` Name string `json:"name"`
Size int64 `json:"size"` Size int64 `json:"size"`
URL string `json:"url"` URL string `json:"url"`
ModTime time.Time `json:"mod_time"` ModTime time.Time `json:"mod_time"`
Mode os.FileMode `json:"mode"` Mode os.FileMode `json:"mode"`
IsDir bool `json:"is_dir"` IsDir bool `json:"is_dir"`
IsSymlink bool `json:"is_symlink"` IsSymlink bool `json:"is_symlink"`
SymlinkPath string `json:"symlink_path,omitempty"`
// a pointer to the template context is useful inside nested templates // a pointer to the template context is useful inside nested templates
Tpl *browseTemplateContext `json:"-"` Tpl *browseTemplateContext `json:"-"`

View file

@ -112,6 +112,15 @@ func (fsrv *FileServer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
} }
fsrv.Browse = new(Browse) fsrv.Browse = new(Browse)
d.Args(&fsrv.Browse.TemplateFile) d.Args(&fsrv.Browse.TemplateFile)
for nesting := d.Nesting(); d.NextBlock(nesting); {
if d.Val() != "reveal_symlinks" {
return d.Errf("unknown subdirective '%s'", d.Val())
}
if fsrv.Browse.RevealSymlinks {
return d.Err("Symlinks path reveal is already enabled")
}
fsrv.Browse.RevealSymlinks = true
}
case "precompressed": case "precompressed":
var order []string var order []string

View file

@ -39,7 +39,7 @@ import (
func init() { func init() {
caddycmd.RegisterCommand(caddycmd.Command{ caddycmd.RegisterCommand(caddycmd.Command{
Name: "file-server", Name: "file-server",
Usage: "[--domain <example.com>] [--root <path>] [--listen <addr>] [--browse] [--access-log] [--precompressed]", Usage: "[--domain <example.com>] [--root <path>] [--listen <addr>] [--browse] [--reveal-symlinks] [--access-log] [--precompressed]",
Short: "Spins up a production-ready file server", Short: "Spins up a production-ready file server",
Long: ` Long: `
A simple but production-ready file server. Useful for quick deployments, A simple but production-ready file server. Useful for quick deployments,
@ -62,6 +62,7 @@ respond with a file listing.`,
cmd.Flags().StringP("root", "r", "", "The path to the root of the site") cmd.Flags().StringP("root", "r", "", "The path to the root of the site")
cmd.Flags().StringP("listen", "l", "", "The address to which to bind the listener") cmd.Flags().StringP("listen", "l", "", "The address to which to bind the listener")
cmd.Flags().BoolP("browse", "b", false, "Enable directory browsing") cmd.Flags().BoolP("browse", "b", false, "Enable directory browsing")
cmd.Flags().BoolP("reveal-symlinks", "", false, "Show symlink paths when browse is enabled.")
cmd.Flags().BoolP("templates", "t", false, "Enable template rendering") cmd.Flags().BoolP("templates", "t", false, "Enable template rendering")
cmd.Flags().BoolP("access-log", "a", false, "Enable the access log") cmd.Flags().BoolP("access-log", "a", false, "Enable the access log")
cmd.Flags().BoolP("debug", "v", false, "Enable verbose debug logs") cmd.Flags().BoolP("debug", "v", false, "Enable verbose debug logs")
@ -91,12 +92,12 @@ func cmdFileServer(fs caddycmd.Flags) (int, error) {
templates := fs.Bool("templates") templates := fs.Bool("templates")
accessLog := fs.Bool("access-log") accessLog := fs.Bool("access-log")
debug := fs.Bool("debug") debug := fs.Bool("debug")
revealSymlinks := fs.Bool("reveal-symlinks")
compress := !fs.Bool("no-compress") compress := !fs.Bool("no-compress")
precompressed, err := fs.GetStringSlice("precompressed") precompressed, err := fs.GetStringSlice("precompressed")
if err != nil { if err != nil {
return caddy.ExitCodeFailedStartup, fmt.Errorf("invalid precompressed flag: %v", err) return caddy.ExitCodeFailedStartup, fmt.Errorf("invalid precompressed flag: %v", err)
} }
var handlers []json.RawMessage var handlers []json.RawMessage
if compress { if compress {
@ -150,7 +151,7 @@ func cmdFileServer(fs caddycmd.Flags) (int, error) {
} }
if browse { if browse {
handler.Browse = new(Browse) handler.Browse = &Browse{RevealSymlinks: revealSymlinks}
} }
handlers = append(handlers, caddyconfig.JSONModuleObject(handler, "handler", "file_server", nil)) handlers = append(handlers, caddyconfig.JSONModuleObject(handler, "handler", "file_server", nil))