Updates to #21
parent
0b5a318728
commit
a74393619f
|
@ -3,6 +3,7 @@
|
||||||
<div class="listing">
|
<div class="listing">
|
||||||
<div class="container" id="listing">
|
<div class="container" id="listing">
|
||||||
{{- range .Items}}
|
{{- range .Items}}
|
||||||
|
{{ if .UserAllowed }}
|
||||||
<div class="item" data-dir="{{- if .IsDir}}true{{ else }}false{{ end }}" id="{{.URL}}">
|
<div class="item" data-dir="{{- if .IsDir}}true{{ else }}false{{ end }}" id="{{.URL}}">
|
||||||
<div>
|
<div>
|
||||||
<a href="{{.URL}}">
|
<a href="{{.URL}}">
|
||||||
|
@ -28,6 +29,7 @@
|
||||||
</div>
|
</div>
|
||||||
<span class="checkbox" data-href="{{.URL}}"><i class="material-icons">check</i>
|
<span class="checkbox" data-href="{{.URL}}"><i class="material-icons">check</i>
|
||||||
</div>
|
</div>
|
||||||
|
{{ end }}
|
||||||
{{- end}}
|
{{- end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -14,37 +14,22 @@ import (
|
||||||
|
|
||||||
// Config is a configuration for browsing in a particualr path.
|
// Config is a configuration for browsing in a particualr path.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
*UserConfig
|
*User
|
||||||
BaseURL string
|
BaseURL string
|
||||||
AbsoluteURL string
|
AbsoluteURL string
|
||||||
AddrPath string
|
AddrPath string
|
||||||
Token string // Anti CSRF token
|
Token string // Anti CSRF token
|
||||||
HugoEnabled bool // Enables the Hugo plugin for File Manager
|
HugoEnabled bool // Enables the Hugo plugin for File Manager
|
||||||
Users map[string]*UserConfig
|
Users map[string]*User
|
||||||
CurrentUser *UserConfig
|
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
|
// Rule is a dissalow/allow rule
|
||||||
type Rule struct {
|
type Rule struct {
|
||||||
Regex bool
|
Regex bool
|
||||||
Allow bool
|
Allow bool
|
||||||
Path string
|
Path string
|
||||||
Rexexp *regexp.Regexp
|
Regexp *regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse parses the configuration set by the user so it can
|
// 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 err error
|
||||||
var cCfg *UserConfig
|
var cCfg *User
|
||||||
var baseURL string
|
var baseURL string
|
||||||
|
|
||||||
for c.Next() {
|
for c.Next() {
|
||||||
var cfg = Config{UserConfig: &UserConfig{}}
|
var cfg = Config{User: &User{}}
|
||||||
cfg.PathScope = "."
|
cfg.PathScope = "."
|
||||||
cfg.Root = http.Dir(cfg.PathScope)
|
cfg.Root = http.Dir(cfg.PathScope)
|
||||||
cfg.BaseURL = ""
|
cfg.BaseURL = ""
|
||||||
cfg.FrontMatter = "yaml"
|
cfg.FrontMatter = "yaml"
|
||||||
cfg.HugoEnabled = false
|
cfg.HugoEnabled = false
|
||||||
cfg.Users = map[string]*UserConfig{}
|
cfg.Users = map[string]*User{}
|
||||||
cfg.AllowCommands = true
|
cfg.AllowCommands = true
|
||||||
cfg.AllowEdit = true
|
cfg.AllowEdit = true
|
||||||
cfg.AllowNew = true
|
cfg.AllowNew = true
|
||||||
cfg.Commands = []string{"git", "svn", "hg"}
|
cfg.Commands = []string{"git", "svn", "hg"}
|
||||||
|
cfg.Rules = []*Rule{&Rule{
|
||||||
|
Regex: true,
|
||||||
|
Allow: false,
|
||||||
|
Regexp: regexp.MustCompile("\\/\\..+"),
|
||||||
|
}}
|
||||||
|
|
||||||
baseURL = ""
|
baseURL = ""
|
||||||
cCfg = cfg.UserConfig
|
cCfg = cfg.User
|
||||||
|
|
||||||
for c.NextBlock() {
|
for c.NextBlock() {
|
||||||
switch c.Val() {
|
switch c.Val() {
|
||||||
|
@ -167,7 +157,7 @@ func Parse(c *caddy.Controller) ([]Config, error) {
|
||||||
Regex: false,
|
Regex: false,
|
||||||
Allow: true,
|
Allow: true,
|
||||||
Path: c.Val(),
|
Path: c.Val(),
|
||||||
Rexexp: nil,
|
Regexp: nil,
|
||||||
})
|
})
|
||||||
case "allow_r":
|
case "allow_r":
|
||||||
if !c.NextArg() {
|
if !c.NextArg() {
|
||||||
|
@ -178,7 +168,7 @@ func Parse(c *caddy.Controller) ([]Config, error) {
|
||||||
Regex: true,
|
Regex: true,
|
||||||
Allow: true,
|
Allow: true,
|
||||||
Path: "",
|
Path: "",
|
||||||
Rexexp: regexp.MustCompile(c.Val()),
|
Regexp: regexp.MustCompile(c.Val()),
|
||||||
})
|
})
|
||||||
case "block":
|
case "block":
|
||||||
if !c.NextArg() {
|
if !c.NextArg() {
|
||||||
|
@ -189,7 +179,7 @@ func Parse(c *caddy.Controller) ([]Config, error) {
|
||||||
Regex: false,
|
Regex: false,
|
||||||
Allow: false,
|
Allow: false,
|
||||||
Path: c.Val(),
|
Path: c.Val(),
|
||||||
Rexexp: nil,
|
Regexp: nil,
|
||||||
})
|
})
|
||||||
case "block_r":
|
case "block_r":
|
||||||
if !c.NextArg() {
|
if !c.NextArg() {
|
||||||
|
@ -200,7 +190,7 @@ func Parse(c *caddy.Controller) ([]Config, error) {
|
||||||
Regex: true,
|
Regex: true,
|
||||||
Allow: false,
|
Allow: false,
|
||||||
Path: "",
|
Path: "",
|
||||||
Rexexp: regexp.MustCompile(c.Val()),
|
Regexp: regexp.MustCompile(c.Val()),
|
||||||
})
|
})
|
||||||
// NEW USER BLOCK?
|
// NEW USER BLOCK?
|
||||||
default:
|
default:
|
||||||
|
@ -212,7 +202,7 @@ func Parse(c *caddy.Controller) ([]Config, error) {
|
||||||
|
|
||||||
// Get the username, sets the current user, and initializes it
|
// Get the username, sets the current user, and initializes it
|
||||||
val = strings.TrimSuffix(val, ":")
|
val = strings.TrimSuffix(val, ":")
|
||||||
cfg.Users[val] = &UserConfig{}
|
cfg.Users[val] = &User{}
|
||||||
|
|
||||||
// Initialize the new user
|
// Initialize the new user
|
||||||
cCfg = cfg.Users[val]
|
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
|
||||||
|
}
|
|
@ -33,11 +33,12 @@ type Info struct {
|
||||||
Content string
|
Content string
|
||||||
Raw []byte
|
Raw []byte
|
||||||
Type string
|
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
|
// GetInfo gets the file information and, in case of error, returns the
|
||||||
// respective HTTP error code
|
// 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
|
var err error
|
||||||
|
|
||||||
rootPath := strings.Replace(url.Path, c.BaseURL, "", 1)
|
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
|
// 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 {
|
if i.IsDir {
|
||||||
return i.serveListing(w, r, c, u)
|
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)
|
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()
|
err := i.GetExtendedInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.ToHTTPCode(err), err
|
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")
|
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
|
var err error
|
||||||
|
|
||||||
file, err := u.Root.Open(i.RootPath)
|
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()
|
defer file.Close()
|
||||||
|
|
||||||
listing, err := i.loadDirectoryContents(file, c)
|
listing, err := i.loadDirectoryContents(file, r.URL.Path, u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
switch {
|
switch {
|
||||||
|
@ -259,17 +260,17 @@ func (i *Info) serveListing(w http.ResponseWriter, r *http.Request, c *config.Co
|
||||||
return page.PrintAsHTML(w, "listing")
|
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)
|
files, err := file.Readdir(-1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
listing := directoryListing(files, i.RootPath)
|
listing := directoryListing(files, i.RootPath, path, u)
|
||||||
return &listing, nil
|
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 (
|
var (
|
||||||
fileinfos []Info
|
fileinfos []Info
|
||||||
dirCount, fileCount int
|
dirCount, fileCount int
|
||||||
|
@ -285,8 +286,8 @@ func directoryListing(files []os.FileInfo, urlPath string) Listing {
|
||||||
fileCount++
|
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{
|
fileinfos = append(fileinfos, Info{
|
||||||
IsDir: f.IsDir(),
|
IsDir: f.IsDir(),
|
||||||
Name: f.Name(),
|
Name: f.Name(),
|
||||||
|
@ -294,6 +295,7 @@ func directoryListing(files []os.FileInfo, urlPath string) Listing {
|
||||||
URL: url.String(),
|
URL: url.String(),
|
||||||
ModTime: f.ModTime().UTC(),
|
ModTime: f.ModTime().UTC(),
|
||||||
Mode: f.Mode(),
|
Mode: f.Mode(),
|
||||||
|
UserAllowed: u.Allowed(url.String()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Update is used to update a file that was edited
|
// 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{}
|
var data map[string]interface{}
|
||||||
kind := r.Header.Get("kind")
|
kind := r.Header.Get("kind")
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
package filemanager
|
package filemanager
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
e "errors"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
@ -41,7 +42,7 @@ func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, err
|
||||||
code int
|
code int
|
||||||
err error
|
err error
|
||||||
serveAssets bool
|
serveAssets bool
|
||||||
user *config.UserConfig
|
user *config.User
|
||||||
)
|
)
|
||||||
|
|
||||||
for i := range f.Configs {
|
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]
|
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 {
|
if r.Method != http.MethodPost && !serveAssets {
|
||||||
fi, code, err = directory.GetInfo(r.URL, c, user)
|
fi, code, err = directory.GetInfo(r.URL, c, user)
|
||||||
if err != nil {
|
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
|
// 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"), " ")
|
command := strings.Split(r.Header.Get("command"), " ")
|
||||||
|
|
||||||
// Check if the command is allowed
|
// Check if the command is allowed
|
||||||
|
|
|
@ -24,7 +24,7 @@ type Info struct {
|
||||||
Name string
|
Name string
|
||||||
Path string
|
Path string
|
||||||
IsDir bool
|
IsDir bool
|
||||||
User *config.UserConfig
|
User *config.User
|
||||||
Config *config.Config
|
Config *config.Config
|
||||||
Data interface{}
|
Data interface{}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue