Updates to #21
This commit is contained in:
		
							parent
							
								
									0b5a318728
								
							
						
					
					
						commit
						a74393619f
					
				| 
						 | 
				
			
			@ -3,6 +3,7 @@
 | 
			
		|||
<div class="listing">
 | 
			
		||||
    <div class="container" id="listing">
 | 
			
		||||
    {{- range .Items}}
 | 
			
		||||
        {{ if .UserAllowed }}
 | 
			
		||||
        <div class="item" data-dir="{{- if .IsDir}}true{{ else }}false{{ end }}" id="{{.URL}}">
 | 
			
		||||
            <div>
 | 
			
		||||
                <a href="{{.URL}}">
 | 
			
		||||
| 
						 | 
				
			
			@ -28,6 +29,7 @@
 | 
			
		|||
            </div>
 | 
			
		||||
            <span class="checkbox" data-href="{{.URL}}"><i class="material-icons">check</i>
 | 
			
		||||
        </div>
 | 
			
		||||
        {{ end }}
 | 
			
		||||
    {{- end}}
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,37 +14,22 @@ import (
 | 
			
		|||
 | 
			
		||||
// Config is a configuration for browsing in a particualr path.
 | 
			
		||||
type Config struct {
 | 
			
		||||
	*UserConfig
 | 
			
		||||
	*User
 | 
			
		||||
	BaseURL     string
 | 
			
		||||
	AbsoluteURL string
 | 
			
		||||
	AddrPath    string
 | 
			
		||||
	Token       string // Anti CSRF token
 | 
			
		||||
	HugoEnabled bool   // Enables the Hugo plugin for File Manager
 | 
			
		||||
	Users       map[string]*UserConfig
 | 
			
		||||
	CurrentUser *UserConfig
 | 
			
		||||
	Users       map[string]*User
 | 
			
		||||
	CurrentUser *User
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UserConfig contains the configuration for each user
 | 
			
		||||
type UserConfig struct {
 | 
			
		||||
	PathScope     string          `json:"-"` // Path the user have access
 | 
			
		||||
	Root          http.FileSystem `json:"-"` // The virtual file system the user have access
 | 
			
		||||
	StyleSheet    string          `json:"-"` // Costum stylesheet
 | 
			
		||||
	FrontMatter   string          `json:"-"` // Default frontmatter to save files in
 | 
			
		||||
	AllowNew      bool            // Can create files and folders
 | 
			
		||||
	AllowEdit     bool            // Can edit/rename files
 | 
			
		||||
	AllowCommands bool            // Can execute commands
 | 
			
		||||
	Commands      []string        // Available Commands
 | 
			
		||||
	Rules         []*Rule         `json:"-"` // Access rules
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// REVIEW: USE USER ROOT
 | 
			
		||||
 | 
			
		||||
// Rule is a dissalow/allow rule
 | 
			
		||||
type Rule struct {
 | 
			
		||||
	Regex  bool
 | 
			
		||||
	Allow  bool
 | 
			
		||||
	Path   string
 | 
			
		||||
	Rexexp *regexp.Regexp
 | 
			
		||||
	Regexp *regexp.Regexp
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Parse parses the configuration set by the user so it can
 | 
			
		||||
| 
						 | 
				
			
			@ -63,24 +48,29 @@ func Parse(c *caddy.Controller) ([]Config, error) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	var cCfg *UserConfig
 | 
			
		||||
	var cCfg *User
 | 
			
		||||
	var baseURL string
 | 
			
		||||
 | 
			
		||||
	for c.Next() {
 | 
			
		||||
		var cfg = Config{UserConfig: &UserConfig{}}
 | 
			
		||||
		var cfg = Config{User: &User{}}
 | 
			
		||||
		cfg.PathScope = "."
 | 
			
		||||
		cfg.Root = http.Dir(cfg.PathScope)
 | 
			
		||||
		cfg.BaseURL = ""
 | 
			
		||||
		cfg.FrontMatter = "yaml"
 | 
			
		||||
		cfg.HugoEnabled = false
 | 
			
		||||
		cfg.Users = map[string]*UserConfig{}
 | 
			
		||||
		cfg.Users = map[string]*User{}
 | 
			
		||||
		cfg.AllowCommands = true
 | 
			
		||||
		cfg.AllowEdit = true
 | 
			
		||||
		cfg.AllowNew = true
 | 
			
		||||
		cfg.Commands = []string{"git", "svn", "hg"}
 | 
			
		||||
		cfg.Rules = []*Rule{&Rule{
 | 
			
		||||
			Regex:  true,
 | 
			
		||||
			Allow:  false,
 | 
			
		||||
			Regexp: regexp.MustCompile("\\/\\..+"),
 | 
			
		||||
		}}
 | 
			
		||||
 | 
			
		||||
		baseURL = ""
 | 
			
		||||
		cCfg = cfg.UserConfig
 | 
			
		||||
		cCfg = cfg.User
 | 
			
		||||
 | 
			
		||||
		for c.NextBlock() {
 | 
			
		||||
			switch c.Val() {
 | 
			
		||||
| 
						 | 
				
			
			@ -167,7 +157,7 @@ func Parse(c *caddy.Controller) ([]Config, error) {
 | 
			
		|||
					Regex:  false,
 | 
			
		||||
					Allow:  true,
 | 
			
		||||
					Path:   c.Val(),
 | 
			
		||||
					Rexexp: nil,
 | 
			
		||||
					Regexp: nil,
 | 
			
		||||
				})
 | 
			
		||||
			case "allow_r":
 | 
			
		||||
				if !c.NextArg() {
 | 
			
		||||
| 
						 | 
				
			
			@ -178,7 +168,7 @@ func Parse(c *caddy.Controller) ([]Config, error) {
 | 
			
		|||
					Regex:  true,
 | 
			
		||||
					Allow:  true,
 | 
			
		||||
					Path:   "",
 | 
			
		||||
					Rexexp: regexp.MustCompile(c.Val()),
 | 
			
		||||
					Regexp: regexp.MustCompile(c.Val()),
 | 
			
		||||
				})
 | 
			
		||||
			case "block":
 | 
			
		||||
				if !c.NextArg() {
 | 
			
		||||
| 
						 | 
				
			
			@ -189,7 +179,7 @@ func Parse(c *caddy.Controller) ([]Config, error) {
 | 
			
		|||
					Regex:  false,
 | 
			
		||||
					Allow:  false,
 | 
			
		||||
					Path:   c.Val(),
 | 
			
		||||
					Rexexp: nil,
 | 
			
		||||
					Regexp: nil,
 | 
			
		||||
				})
 | 
			
		||||
			case "block_r":
 | 
			
		||||
				if !c.NextArg() {
 | 
			
		||||
| 
						 | 
				
			
			@ -200,7 +190,7 @@ func Parse(c *caddy.Controller) ([]Config, error) {
 | 
			
		|||
					Regex:  true,
 | 
			
		||||
					Allow:  false,
 | 
			
		||||
					Path:   "",
 | 
			
		||||
					Rexexp: regexp.MustCompile(c.Val()),
 | 
			
		||||
					Regexp: regexp.MustCompile(c.Val()),
 | 
			
		||||
				})
 | 
			
		||||
			// NEW USER BLOCK?
 | 
			
		||||
			default:
 | 
			
		||||
| 
						 | 
				
			
			@ -212,7 +202,7 @@ func Parse(c *caddy.Controller) ([]Config, error) {
 | 
			
		|||
 | 
			
		||||
				// Get the username, sets the current user, and initializes it
 | 
			
		||||
				val = strings.TrimSuffix(val, ":")
 | 
			
		||||
				cfg.Users[val] = &UserConfig{}
 | 
			
		||||
				cfg.Users[val] = &User{}
 | 
			
		||||
 | 
			
		||||
				// Initialize the new user
 | 
			
		||||
				cCfg = cfg.Users[val]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,43 @@
 | 
			
		|||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// User contains the configuration for each user
 | 
			
		||||
type User struct {
 | 
			
		||||
	PathScope     string          `json:"-"` // Path the user have access
 | 
			
		||||
	Root          http.FileSystem `json:"-"` // The virtual file system the user have access
 | 
			
		||||
	StyleSheet    string          `json:"-"` // Costum stylesheet
 | 
			
		||||
	FrontMatter   string          `json:"-"` // Default frontmatter to save files in
 | 
			
		||||
	AllowNew      bool            // Can create files and folders
 | 
			
		||||
	AllowEdit     bool            // Can edit/rename files
 | 
			
		||||
	AllowCommands bool            // Can execute commands
 | 
			
		||||
	Commands      []string        // Available Commands
 | 
			
		||||
	Rules         []*Rule         `json:"-"` // Access rules
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// REVIEW: USE USER ROOT
 | 
			
		||||
 | 
			
		||||
// Allowed is
 | 
			
		||||
func (u User) Allowed(url string) bool {
 | 
			
		||||
	var rule *Rule
 | 
			
		||||
	i := len(u.Rules) - 1
 | 
			
		||||
 | 
			
		||||
	for i >= 0 {
 | 
			
		||||
		rule = u.Rules[i]
 | 
			
		||||
 | 
			
		||||
		if rule.Regex {
 | 
			
		||||
			if rule.Regexp.MatchString(url) {
 | 
			
		||||
				return rule.Allow
 | 
			
		||||
			}
 | 
			
		||||
		} else if strings.HasPrefix(url, rule.Path) {
 | 
			
		||||
			return rule.Allow
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		i--
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -21,23 +21,24 @@ import (
 | 
			
		|||
 | 
			
		||||
// Info is the information about a particular file or directory
 | 
			
		||||
type Info struct {
 | 
			
		||||
	IsDir    bool
 | 
			
		||||
	Name     string
 | 
			
		||||
	Size     int64
 | 
			
		||||
	URL      string
 | 
			
		||||
	Path     string // The relative Path of the file/directory relative to Caddyfile.
 | 
			
		||||
	RootPath string // The Path of the file/directory on http.FileSystem.
 | 
			
		||||
	ModTime  time.Time
 | 
			
		||||
	Mode     os.FileMode
 | 
			
		||||
	Mimetype string
 | 
			
		||||
	Content  string
 | 
			
		||||
	Raw      []byte
 | 
			
		||||
	Type     string
 | 
			
		||||
	IsDir       bool
 | 
			
		||||
	Name        string
 | 
			
		||||
	Size        int64
 | 
			
		||||
	URL         string
 | 
			
		||||
	Path        string // The relative Path of the file/directory relative to Caddyfile.
 | 
			
		||||
	RootPath    string // The Path of the file/directory on http.FileSystem.
 | 
			
		||||
	ModTime     time.Time
 | 
			
		||||
	Mode        os.FileMode
 | 
			
		||||
	Mimetype    string
 | 
			
		||||
	Content     string
 | 
			
		||||
	Raw         []byte
 | 
			
		||||
	Type        string
 | 
			
		||||
	UserAllowed bool // Indicates if the user has permissions to open this directory
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetInfo gets the file information and, in case of error, returns the
 | 
			
		||||
// respective HTTP error code
 | 
			
		||||
func GetInfo(url *url.URL, c *config.Config, u *config.UserConfig) (*Info, int, error) {
 | 
			
		||||
func GetInfo(url *url.URL, c *config.Config, u *config.User) (*Info, int, error) {
 | 
			
		||||
	var err error
 | 
			
		||||
 | 
			
		||||
	rootPath := strings.Replace(url.Path, c.BaseURL, "", 1)
 | 
			
		||||
| 
						 | 
				
			
			@ -142,7 +143,7 @@ func (i *Info) Rename(w http.ResponseWriter, r *http.Request) (int, error) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// ServeAsHTML is used to serve single file pages
 | 
			
		||||
func (i *Info) ServeAsHTML(w http.ResponseWriter, r *http.Request, c *config.Config, u *config.UserConfig) (int, error) {
 | 
			
		||||
func (i *Info) ServeAsHTML(w http.ResponseWriter, r *http.Request, c *config.Config, u *config.User) (int, error) {
 | 
			
		||||
	if i.IsDir {
 | 
			
		||||
		return i.serveListing(w, r, c, u)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -150,7 +151,7 @@ func (i *Info) ServeAsHTML(w http.ResponseWriter, r *http.Request, c *config.Con
 | 
			
		|||
	return i.serveSingleFile(w, r, c, u)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (i *Info) serveSingleFile(w http.ResponseWriter, r *http.Request, c *config.Config, u *config.UserConfig) (int, error) {
 | 
			
		||||
func (i *Info) serveSingleFile(w http.ResponseWriter, r *http.Request, c *config.Config, u *config.User) (int, error) {
 | 
			
		||||
	err := i.GetExtendedInfo()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.ToHTTPCode(err), err
 | 
			
		||||
| 
						 | 
				
			
			@ -185,7 +186,7 @@ func (i *Info) serveSingleFile(w http.ResponseWriter, r *http.Request, c *config
 | 
			
		|||
	return page.PrintAsHTML(w, "single")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (i *Info) serveListing(w http.ResponseWriter, r *http.Request, c *config.Config, u *config.UserConfig) (int, error) {
 | 
			
		||||
func (i *Info) serveListing(w http.ResponseWriter, r *http.Request, c *config.Config, u *config.User) (int, error) {
 | 
			
		||||
	var err error
 | 
			
		||||
 | 
			
		||||
	file, err := u.Root.Open(i.RootPath)
 | 
			
		||||
| 
						 | 
				
			
			@ -194,7 +195,7 @@ func (i *Info) serveListing(w http.ResponseWriter, r *http.Request, c *config.Co
 | 
			
		|||
	}
 | 
			
		||||
	defer file.Close()
 | 
			
		||||
 | 
			
		||||
	listing, err := i.loadDirectoryContents(file, c)
 | 
			
		||||
	listing, err := i.loadDirectoryContents(file, r.URL.Path, u)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println(err)
 | 
			
		||||
		switch {
 | 
			
		||||
| 
						 | 
				
			
			@ -259,17 +260,17 @@ func (i *Info) serveListing(w http.ResponseWriter, r *http.Request, c *config.Co
 | 
			
		|||
	return page.PrintAsHTML(w, "listing")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (i Info) loadDirectoryContents(file http.File, c *config.Config) (*Listing, error) {
 | 
			
		||||
func (i Info) loadDirectoryContents(file http.File, path string, u *config.User) (*Listing, error) {
 | 
			
		||||
	files, err := file.Readdir(-1)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	listing := directoryListing(files, i.RootPath)
 | 
			
		||||
	listing := directoryListing(files, i.RootPath, path, u)
 | 
			
		||||
	return &listing, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func directoryListing(files []os.FileInfo, urlPath string) Listing {
 | 
			
		||||
func directoryListing(files []os.FileInfo, urlPath string, basePath string, u *config.User) Listing {
 | 
			
		||||
	var (
 | 
			
		||||
		fileinfos           []Info
 | 
			
		||||
		dirCount, fileCount int
 | 
			
		||||
| 
						 | 
				
			
			@ -285,15 +286,16 @@ func directoryListing(files []os.FileInfo, urlPath string) Listing {
 | 
			
		|||
			fileCount++
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		url := url.URL{Path: "./" + name} // prepend with "./" to fix paths with ':' in the name
 | 
			
		||||
 | 
			
		||||
		// Absolute URL
 | 
			
		||||
		url := url.URL{Path: basePath + name}
 | 
			
		||||
		fileinfos = append(fileinfos, Info{
 | 
			
		||||
			IsDir:   f.IsDir(),
 | 
			
		||||
			Name:    f.Name(),
 | 
			
		||||
			Size:    f.Size(),
 | 
			
		||||
			URL:     url.String(),
 | 
			
		||||
			ModTime: f.ModTime().UTC(),
 | 
			
		||||
			Mode:    f.Mode(),
 | 
			
		||||
			IsDir:       f.IsDir(),
 | 
			
		||||
			Name:        f.Name(),
 | 
			
		||||
			Size:        f.Size(),
 | 
			
		||||
			URL:         url.String(),
 | 
			
		||||
			ModTime:     f.ModTime().UTC(),
 | 
			
		||||
			Mode:        f.Mode(),
 | 
			
		||||
			UserAllowed: u.Allowed(url.String()),
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,7 +15,7 @@ import (
 | 
			
		|||
)
 | 
			
		||||
 | 
			
		||||
// Update is used to update a file that was edited
 | 
			
		||||
func (i *Info) Update(w http.ResponseWriter, r *http.Request, c *config.Config, u *config.UserConfig) (int, error) {
 | 
			
		||||
func (i *Info) Update(w http.ResponseWriter, r *http.Request, c *config.Config, u *config.User) (int, error) {
 | 
			
		||||
	var data map[string]interface{}
 | 
			
		||||
	kind := r.Header.Get("kind")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,7 @@
 | 
			
		|||
package filemanager
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	e "errors"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"log"
 | 
			
		||||
| 
						 | 
				
			
			@ -41,7 +42,7 @@ func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, err
 | 
			
		|||
		code        int
 | 
			
		||||
		err         error
 | 
			
		||||
		serveAssets bool
 | 
			
		||||
		user        *config.UserConfig
 | 
			
		||||
		user        *config.User
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	for i := range f.Configs {
 | 
			
		||||
| 
						 | 
				
			
			@ -56,6 +57,14 @@ func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, err
 | 
			
		|||
				user = c.Users[username]
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if !user.Allowed(r.URL.Path) {
 | 
			
		||||
				if r.Method == http.MethodGet {
 | 
			
		||||
					return errors.PrintHTML(w, http.StatusForbidden, e.New("You don't have permission to access this page."))
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				return http.StatusForbidden, nil
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if r.Method != http.MethodPost && !serveAssets {
 | 
			
		||||
				fi, code, err = directory.GetInfo(r.URL, c, user)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -241,7 +250,7 @@ func newDirectory(w http.ResponseWriter, r *http.Request, c *config.Config) (int
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// command handles the requests for VCS related commands: git, svn and mercurial
 | 
			
		||||
func command(w http.ResponseWriter, r *http.Request, c *config.Config, u *config.UserConfig) (int, error) {
 | 
			
		||||
func command(w http.ResponseWriter, r *http.Request, c *config.Config, u *config.User) (int, error) {
 | 
			
		||||
	command := strings.Split(r.Header.Get("command"), " ")
 | 
			
		||||
 | 
			
		||||
	// Check if the command is allowed
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,7 +24,7 @@ type Info struct {
 | 
			
		|||
	Name   string
 | 
			
		||||
	Path   string
 | 
			
		||||
	IsDir  bool
 | 
			
		||||
	User   *config.UserConfig
 | 
			
		||||
	User   *config.User
 | 
			
		||||
	Config *config.Config
 | 
			
		||||
	Data   interface{}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue