diff options
author | Aziz Rmadi <[email protected]> | 2024-02-05 22:31:26 -0600 |
---|---|---|
committer | GitHub <[email protected]> | 2024-02-06 04:31:26 +0000 |
commit | feb07a7b59a3ed6f518e5ce62f29d4816a51c5e7 (patch) | |
tree | 49695ae82fcf53b2b139b758a46739c020a584a5 /modules | |
parent | a7479302fc0ce40e265fa02130d6f251e6b9de8c (diff) | |
download | caddy-feb07a7b59a3ed6f518e5ce62f29d4816a51c5e7.tar.gz caddy-feb07a7b59a3ed6f518e5ce62f29d4816a51c5e7.zip |
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 <[email protected]>
Diffstat (limited to 'modules')
-rw-r--r-- | modules/caddyhttp/fileserver/browse.go | 2 | ||||
-rw-r--r-- | modules/caddyhttp/fileserver/browse.html | 8 | ||||
-rw-r--r-- | modules/caddyhttp/fileserver/browsetplcontext.go | 42 | ||||
-rw-r--r-- | modules/caddyhttp/fileserver/caddyfile.go | 9 | ||||
-rw-r--r-- | modules/caddyhttp/fileserver/command.go | 7 |
5 files changed, 50 insertions, 18 deletions
diff --git a/modules/caddyhttp/fileserver/browse.go b/modules/caddyhttp/fileserver/browse.go index 7602f3a9e..86adc7e39 100644 --- a/modules/caddyhttp/fileserver/browse.go +++ b/modules/caddyhttp/fileserver/browse.go @@ -50,6 +50,8 @@ var BrowseTemplate string type Browse struct { // Filename of the template to use instead of the embedded browse template. 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 { diff --git a/modules/caddyhttp/fileserver/browse.html b/modules/caddyhttp/fileserver/browse.html index af1772bfd..484d90020 100644 --- a/modules/caddyhttp/fileserver/browse.html +++ b/modules/caddyhttp/fileserver/browse.html @@ -962,7 +962,15 @@ footer { <td> <a href="{{html .URL}}"> {{template "icon" .}} + {{- if not .SymlinkPath}} <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> </td> {{- if .IsDir}} diff --git a/modules/caddyhttp/fileserver/browsetplcontext.go b/modules/caddyhttp/fileserver/browsetplcontext.go index 81db3d06b..5456f0597 100644 --- a/modules/caddyhttp/fileserver/browsetplcontext.go +++ b/modules/caddyhttp/fileserver/browsetplcontext.go @@ -20,6 +20,7 @@ import ( "net/url" "os" "path" + "path/filepath" "sort" "strconv" "strings" @@ -74,12 +75,21 @@ func (fsrv *FileServer) directoryListing(ctx context.Context, fileSystem fs.FS, size := info.Size() fileIsSymlink := isSymlink(info) + symlinkPath := "" if fileIsSymlink { path := caddyhttp.SanitizedPathJoin(root, path.Join(urlPath, info.Name())) fileInfo, err := fs.Stat(fileSystem, path) if err == nil { 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, // which isn't entirely unusual and shouldn't fail the listing. // 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 tplCtx.Items = append(tplCtx.Items, fileInfo{ - IsDir: isDir, - IsSymlink: fileIsSymlink, - Name: name, - Size: size, - URL: u.String(), - ModTime: info.ModTime().UTC(), - Mode: info.Mode(), - Tpl: tplCtx, // a reference up to the template context is useful + IsDir: isDir, + IsSymlink: fileIsSymlink, + Name: name, + Size: size, + URL: u.String(), + ModTime: info.ModTime().UTC(), + Mode: info.Mode(), + Tpl: tplCtx, // a reference up to the template context is useful + SymlinkPath: symlinkPath, }) } @@ -230,13 +241,14 @@ type crumb struct { // fileInfo contains serializable information // about a file or directory. type fileInfo struct { - Name string `json:"name"` - Size int64 `json:"size"` - URL string `json:"url"` - ModTime time.Time `json:"mod_time"` - Mode os.FileMode `json:"mode"` - IsDir bool `json:"is_dir"` - IsSymlink bool `json:"is_symlink"` + Name string `json:"name"` + Size int64 `json:"size"` + URL string `json:"url"` + ModTime time.Time `json:"mod_time"` + Mode os.FileMode `json:"mode"` + IsDir bool `json:"is_dir"` + IsSymlink bool `json:"is_symlink"` + SymlinkPath string `json:"symlink_path,omitempty"` // a pointer to the template context is useful inside nested templates Tpl *browseTemplateContext `json:"-"` diff --git a/modules/caddyhttp/fileserver/caddyfile.go b/modules/caddyhttp/fileserver/caddyfile.go index b18bcdcef..6ad9190f3 100644 --- a/modules/caddyhttp/fileserver/caddyfile.go +++ b/modules/caddyhttp/fileserver/caddyfile.go @@ -112,6 +112,15 @@ func (fsrv *FileServer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { } fsrv.Browse = new(Browse) 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": var order []string diff --git a/modules/caddyhttp/fileserver/command.go b/modules/caddyhttp/fileserver/command.go index 2bc816743..a76998405 100644 --- a/modules/caddyhttp/fileserver/command.go +++ b/modules/caddyhttp/fileserver/command.go @@ -39,7 +39,7 @@ import ( func init() { caddycmd.RegisterCommand(caddycmd.Command{ 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", Long: ` 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("listen", "l", "", "The address to which to bind the listener") 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("access-log", "a", false, "Enable the access log") 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") accessLog := fs.Bool("access-log") debug := fs.Bool("debug") + revealSymlinks := fs.Bool("reveal-symlinks") compress := !fs.Bool("no-compress") precompressed, err := fs.GetStringSlice("precompressed") if err != nil { return caddy.ExitCodeFailedStartup, fmt.Errorf("invalid precompressed flag: %v", err) } - var handlers []json.RawMessage if compress { @@ -150,7 +151,7 @@ func cmdFileServer(fs caddycmd.Flags) (int, error) { } if browse { - handler.Browse = new(Browse) + handler.Browse = &Browse{RevealSymlinks: revealSymlinks} } handlers = append(handlers, caddyconfig.JSONModuleObject(handler, "handler", "file_server", nil)) |