add comments and update config; close #23

This commit is contained in:
Henrique Dias 2016-08-22 12:09:18 +01:00
parent e606af4eae
commit 1df11145a8
3 changed files with 81 additions and 101 deletions

View File

@ -35,7 +35,11 @@ type Rule struct {
// Parse parses the configuration set by the user so it can // Parse parses the configuration set by the user so it can
// be used by the middleware // be used by the middleware
func Parse(c *caddy.Controller) ([]Config, error) { func Parse(c *caddy.Controller) ([]Config, error) {
var configs []Config var (
configs []Config
err error
user *User
)
appendConfig := func(cfg Config) error { appendConfig := func(cfg Config) error {
for _, c := range configs { for _, c := range configs {
@ -47,12 +51,9 @@ func Parse(c *caddy.Controller) ([]Config, error) {
return nil return nil
} }
var err error
var cCfg *User
var baseURL string
for c.Next() { for c.Next() {
var cfg = Config{User: &User{}} // Initialize the configuration with the default settings
cfg := Config{User: &User{}}
cfg.PathScope = "." cfg.PathScope = "."
cfg.Root = http.Dir(cfg.PathScope) cfg.Root = http.Dir(cfg.PathScope)
cfg.BaseURL = "" cfg.BaseURL = ""
@ -69,8 +70,8 @@ func Parse(c *caddy.Controller) ([]Config, error) {
Regexp: regexp.MustCompile("\\/\\..+"), Regexp: regexp.MustCompile("\\/\\..+"),
}} }}
baseURL = "" // Set the first user, the global user
cCfg = cfg.User user = cfg.User
for c.NextBlock() { for c.NextBlock() {
switch c.Val() { switch c.Val() {
@ -78,37 +79,42 @@ func Parse(c *caddy.Controller) ([]Config, error) {
if !c.NextArg() { if !c.NextArg() {
return configs, c.ArgErr() return configs, c.ArgErr()
} }
baseURL = c.Val()
cfg.BaseURL = c.Val()
case "frontmatter": case "frontmatter":
if !c.NextArg() { if !c.NextArg() {
return configs, c.ArgErr() return configs, c.ArgErr()
} }
cCfg.FrontMatter = c.Val()
if cCfg.FrontMatter != "yaml" && cCfg.FrontMatter != "json" && cCfg.FrontMatter != "toml" { user.FrontMatter = c.Val()
if user.FrontMatter != "yaml" && user.FrontMatter != "json" && user.FrontMatter != "toml" {
return configs, c.Err("frontmatter type not supported") return configs, c.Err("frontmatter type not supported")
} }
case "show": case "show":
if !c.NextArg() { if !c.NextArg() {
return configs, c.ArgErr() return configs, c.ArgErr()
} }
cCfg.PathScope = c.Val()
cCfg.PathScope = strings.TrimSuffix(cCfg.PathScope, "/") user.PathScope = c.Val()
cCfg.Root = http.Dir(cCfg.PathScope) user.PathScope = strings.TrimSuffix(user.PathScope, "/")
user.Root = http.Dir(user.PathScope)
case "styles": case "styles":
if !c.NextArg() { if !c.NextArg() {
return configs, c.ArgErr() return configs, c.ArgErr()
} }
var tplBytes []byte var tplBytes []byte
tplBytes, err = ioutil.ReadFile(c.Val()) tplBytes, err = ioutil.ReadFile(c.Val())
if err != nil { if err != nil {
return configs, err return configs, err
} }
cCfg.StyleSheet = string(tplBytes) user.StyleSheet = string(tplBytes)
case "allow_new": case "allow_new":
if !c.NextArg() { if !c.NextArg() {
return configs, c.ArgErr() return configs, c.ArgErr()
} }
cCfg.AllowNew, err = strconv.ParseBool(c.Val())
user.AllowNew, err = strconv.ParseBool(c.Val())
if err != nil { if err != nil {
return configs, err return configs, err
} }
@ -116,7 +122,8 @@ func Parse(c *caddy.Controller) ([]Config, error) {
if !c.NextArg() { if !c.NextArg() {
return configs, c.ArgErr() return configs, c.ArgErr()
} }
cCfg.AllowEdit, err = strconv.ParseBool(c.Val())
user.AllowEdit, err = strconv.ParseBool(c.Val())
if err != nil { if err != nil {
return configs, err return configs, err
} }
@ -124,7 +131,8 @@ func Parse(c *caddy.Controller) ([]Config, error) {
if !c.NextArg() { if !c.NextArg() {
return configs, c.ArgErr() return configs, c.ArgErr()
} }
cCfg.AllowCommands, err = strconv.ParseBool(c.Val())
user.AllowCommands, err = strconv.ParseBool(c.Val())
if err != nil { if err != nil {
return configs, err return configs, err
} }
@ -133,7 +141,7 @@ func Parse(c *caddy.Controller) ([]Config, error) {
return configs, c.ArgErr() return configs, c.ArgErr()
} }
cCfg.Commands = append(cCfg.Commands, c.Val()) user.Commands = append(user.Commands, c.Val())
case "block_command": case "block_command":
if !c.NextArg() { if !c.NextArg() {
return configs, c.ArgErr() return configs, c.ArgErr()
@ -141,76 +149,42 @@ func Parse(c *caddy.Controller) ([]Config, error) {
index := 0 index := 0
for i, val := range cCfg.Commands { for i, val := range user.Commands {
if val == c.Val() { if val == c.Val() {
index = i index = i
} }
} }
cCfg.Commands = append(cCfg.Commands[:index], cCfg.Commands[index+1:]...) user.Commands = append(user.Commands[:index], user.Commands[index+1:]...)
case "allow": case "allow", "allow_r", "block", "block_r":
ruleType := c.Val()
if !c.NextArg() { if !c.NextArg() {
return configs, c.ArgErr() return configs, c.ArgErr()
} }
if c.Val() == "dotfiles" { if c.Val() == "dotfiles" && !strings.HasSuffix(ruleType, "_r") {
cCfg.Rules = append(cCfg.Rules, &Rule{ ruleType += "_r"
Regex: true, }
Allow: true,
Regexp: regexp.MustCompile("\\/\\..+"), rule := &Rule{
}) Allow: ruleType == "allow" || ruleType == "allow_r",
Regex: ruleType == "allow_r" || ruleType == "block_r",
}
if rule.Regex && c.Val() == "dotfiles" {
rule.Regexp = regexp.MustCompile("\\/\\..+")
} else if rule.Regex {
rule.Regexp = regexp.MustCompile(c.Val())
} else { } else {
cCfg.Rules = append(cCfg.Rules, &Rule{ rule.Path = c.Val()
Regex: false,
Allow: true,
Path: c.Val(),
Regexp: nil,
})
}
case "allow_r":
if !c.NextArg() {
return configs, c.ArgErr()
} }
cCfg.Rules = append(cCfg.Rules, &Rule{ user.Rules = append(user.Rules, rule)
Regex: true,
Allow: true,
Path: "",
Regexp: regexp.MustCompile(c.Val()),
})
case "block":
if !c.NextArg() {
return configs, c.ArgErr()
}
if c.Val() == "dotfiles" {
cCfg.Rules = append(cCfg.Rules, &Rule{
Regex: true,
Allow: false,
Regexp: regexp.MustCompile("\\/\\..+"),
})
} else {
cCfg.Rules = append(cCfg.Rules, &Rule{
Regex: false,
Allow: false,
Path: c.Val(),
Regexp: nil,
})
}
case "block_r":
if !c.NextArg() {
return configs, c.ArgErr()
}
cCfg.Rules = append(cCfg.Rules, &Rule{
Regex: true,
Allow: false,
Path: "",
Regexp: regexp.MustCompile(c.Val()),
})
// NEW USER BLOCK? // NEW USER BLOCK?
default: default:
val := c.Val() val := c.Val()
// Checks if it's a new user // Checks if it's a new user
if !strings.HasSuffix(val, ":") { if !strings.HasSuffix(val, ":") {
fmt.Println("Unknown option " + val) fmt.Println("Unknown option " + val)
@ -221,21 +195,19 @@ func Parse(c *caddy.Controller) ([]Config, error) {
cfg.Users[val] = &User{} cfg.Users[val] = &User{}
// Initialize the new user // Initialize the new user
cCfg = cfg.Users[val] user = cfg.Users[val]
cCfg.AllowCommands = cfg.AllowCommands user.AllowCommands = cfg.AllowCommands
cCfg.AllowEdit = cfg.AllowEdit user.AllowEdit = cfg.AllowEdit
cCfg.AllowNew = cfg.AllowEdit user.AllowNew = cfg.AllowEdit
cCfg.Commands = cfg.Commands user.Commands = cfg.Commands
cCfg.FrontMatter = cfg.FrontMatter user.FrontMatter = cfg.FrontMatter
cCfg.PathScope = cfg.PathScope user.PathScope = cfg.PathScope
cCfg.Root = cfg.Root user.Root = cfg.Root
cCfg.Rules = cfg.Rules user.Rules = cfg.Rules
cCfg.StyleSheet = cfg.StyleSheet user.StyleSheet = cfg.StyleSheet
} }
} }
// Set global base url
cfg.BaseURL = baseURL
cfg.BaseURL = strings.TrimPrefix(cfg.BaseURL, "/") cfg.BaseURL = strings.TrimPrefix(cfg.BaseURL, "/")
cfg.BaseURL = strings.TrimSuffix(cfg.BaseURL, "/") cfg.BaseURL = strings.TrimSuffix(cfg.BaseURL, "/")
cfg.BaseURL = "/" + cfg.BaseURL cfg.BaseURL = "/" + cfg.BaseURL
@ -248,7 +220,6 @@ func Parse(c *caddy.Controller) ([]Config, error) {
if err := appendConfig(cfg); err != nil { if err := appendConfig(cfg); err != nil {
return configs, err return configs, err
} }
} }
return configs, nil return configs, nil

View File

@ -18,9 +18,7 @@ type User struct {
Rules []*Rule `json:"-"` // Access rules Rules []*Rule `json:"-"` // Access rules
} }
// REVIEW: USE USER ROOT // Allowed checks if the user has permission to access a directory/file
// Allowed is
func (u User) Allowed(url string) bool { func (u User) Allowed(url string) bool {
var rule *Rule var rule *Rule
i := len(u.Rules) - 1 i := len(u.Rules) - 1

View File

@ -50,13 +50,16 @@ func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, err
c = &f.Configs[i] c = &f.Configs[i]
serveAssets = httpserver.Path(r.URL.Path).Matches(c.BaseURL + assets.BaseURL) serveAssets = httpserver.Path(r.URL.Path).Matches(c.BaseURL + assets.BaseURL)
// Set the current User // Set the current user.
username, _, _ := r.BasicAuth() username, _, _ := r.BasicAuth()
if _, ok := c.Users[username]; ok { if _, ok := c.Users[username]; ok {
user = c.Users[username] user = c.Users[username]
} else {
user = c.User
} }
// Checks if the user has permission to access the current directory.
if !user.Allowed(r.URL.Path) { if !user.Allowed(r.URL.Path) {
if r.Method == http.MethodGet { if r.Method == http.MethodGet {
return errors.PrintHTML(w, http.StatusForbidden, e.New("You don't have permission to access this page.")) return errors.PrintHTML(w, http.StatusForbidden, e.New("You don't have permission to access this page."))
@ -65,7 +68,10 @@ func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, err
return http.StatusForbidden, nil return http.StatusForbidden, nil
} }
// If this request is neither to server assets, nor to upload/create
// a new file or directory.
if r.Method != http.MethodPost && !serveAssets { if r.Method != http.MethodPost && !serveAssets {
// Gets the information of the directory/file
fi, code, err = directory.GetInfo(r.URL, c, user) fi, code, err = directory.GetInfo(r.URL, c, user)
if err != nil { if err != nil {
if r.Method == http.MethodGet { if r.Method == http.MethodGet {
@ -74,28 +80,30 @@ func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, err
return code, err return code, err
} }
// If it's a dir and the path doesn't end with a trailing slash,
// redirect the user.
if fi.IsDir && !strings.HasSuffix(r.URL.Path, "/") { if fi.IsDir && !strings.HasSuffix(r.URL.Path, "/") {
http.Redirect(w, r, c.AddrPath+r.URL.Path+"/", http.StatusTemporaryRedirect) http.Redirect(w, r, c.AddrPath+r.URL.Path+"/", http.StatusTemporaryRedirect)
return 0, nil return 0, nil
} }
} }
// Secure agains CSRF attacks // Security measures against CSRF attacks.
if r.Method != http.MethodGet { if r.Method != http.MethodGet {
if !c.CheckToken(r) { if !c.CheckToken(r) {
return http.StatusForbidden, nil return http.StatusForbidden, nil
} }
} }
// Route the request depending on the HTTP Method // Route the request depending on the HTTP Method.
switch r.Method { switch r.Method {
case http.MethodGet: case http.MethodGet:
// Read and show directory or file // Read and show directory or file.
if serveAssets { if serveAssets {
return assets.Serve(w, r, c) return assets.Serve(w, r, c)
} }
// Generate anti security token // Generate anti security token.
c.GenerateToken() c.GenerateToken()
if !fi.IsDir { if !fi.IsDir {
@ -124,10 +132,10 @@ func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, err
return http.StatusUnauthorized, nil return http.StatusUnauthorized, nil
} }
// Update a file // Update a file.
return fi.Update(w, r, c, user) return fi.Update(w, r, c, user)
case http.MethodPost: case http.MethodPost:
// Upload a new file // Upload a new file.
if r.Header.Get("Upload") == "true" { if r.Header.Get("Upload") == "true" {
if !user.AllowNew { if !user.AllowNew {
return http.StatusUnauthorized, nil return http.StatusUnauthorized, nil
@ -135,11 +143,13 @@ func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, err
return upload(w, r, c) return upload(w, r, c)
} }
// Search and git commands
// Search and git commands.
if r.Header.Get("Search") == "true" { if r.Header.Get("Search") == "true" {
// TODO: search commands // TODO: search commands.
} }
// VCS commands
// VCS commands.
if r.Header.Get("Command") != "" { if r.Header.Get("Command") != "" {
if !user.AllowCommands { if !user.AllowCommands {
return http.StatusUnauthorized, nil return http.StatusUnauthorized, nil
@ -147,7 +157,8 @@ func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, err
return command(w, r, c, user) return command(w, r, c, user)
} }
// Creates a new folder
// Creates a new folder.
return newDirectory(w, r, c) return newDirectory(w, r, c)
case http.MethodDelete: case http.MethodDelete:
if !user.AllowEdit { if !user.AllowEdit {