webdav-server/cmd/webdav/main.go
Henrique Dias ac0e137b1d
Close #1
2017-08-29 15:14:41 +01:00

240 lines
4.3 KiB
Go

package main
import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"path/filepath"
"regexp"
"github.com/hacdias/webdav"
wd "golang.org/x/net/webdav"
yaml "gopkg.in/yaml.v2"
)
var (
config string
defaultConfigs = []string{
"config.json",
"config.yaml",
"config.yml",
"/etc/webdav/config.json",
"/etc/webdav/config.yaml",
"/etc/webdav/config.yml",
}
)
func init() {
flag.StringVar(&config, "config", "", "Configuration file")
}
func parseRules(raw []map[string]interface{}) []*webdav.Rule {
rules := []*webdav.Rule{}
for _, r := range raw {
rule := &webdav.Rule{
Regex: false,
Allow: false,
Path: "",
}
if regex, ok := r["regex"].(bool); ok {
rule.Regex = regex
}
if allow, ok := r["allow"].(bool); ok {
rule.Allow = allow
}
path, ok := r["rule"].(string)
if !ok {
continue
}
if rule.Regex {
rule.Regexp = regexp.MustCompile(path)
} else {
rule.Path = path
}
rules = append(rules, rule)
}
return rules
}
func parseUsers(raw []map[string]interface{}, c *cfg) {
for _, r := range raw {
username, ok := r["username"].(string)
if !ok {
log.Fatal("user needs an username")
}
password, ok := r["password"].(string)
if !ok {
log.Fatal("user needs a password")
}
c.auth[username] = password
user := &webdav.User{
Scope: c.webdav.User.Scope,
Modify: c.webdav.User.Modify,
Rules: c.webdav.User.Rules,
}
if scope, ok := r["scope"].(string); ok {
user.Scope = scope
}
if modify, ok := r["modify"].(bool); ok {
user.Modify = modify
}
if rules, ok := r["rules"].([]map[string]interface{}); ok {
user.Rules = parseRules(rules)
}
user.Handler = &wd.Handler{
FileSystem: wd.Dir(user.Scope),
LockSystem: wd.NewMemLS(),
}
c.webdav.Users[username] = user
}
}
func getConfig() []byte {
if config == "" {
for _, v := range defaultConfigs {
_, err := os.Stat(v)
if err == nil {
config = v
break
}
}
}
if config == "" {
log.Fatal("no config file specified; couldn't find any config.{yaml,json}")
}
file, err := ioutil.ReadFile(config)
if err != nil {
log.Fatal(err)
}
return file
}
type cfg struct {
webdav *webdav.Config
port string
auth map[string]string
}
func parseConfig() *cfg {
file := getConfig()
data := struct {
Port string `json:"port" yaml:"port"`
Scope string `json:"scope" yaml:"scope"`
Modify bool `json:"modify" yaml:"modify"`
Rules []map[string]interface{} `json:"rules" yaml:"rules"`
Users []map[string]interface{} `json:"users" yaml:"users"`
}{
Port: "0",
Scope: "./",
Modify: true,
}
var err error
if filepath.Ext(config) == ".json" {
err = json.Unmarshal(file, &data)
} else {
err = yaml.Unmarshal(file, &data)
}
if err != nil {
log.Fatal(err)
}
config := &cfg{
port: data.Port,
auth: map[string]string{},
webdav: &webdav.Config{
User: &webdav.User{
Scope: data.Scope,
Modify: data.Modify,
Rules: []*webdav.Rule{},
Handler: &wd.Handler{
FileSystem: wd.Dir(data.Scope),
LockSystem: wd.NewMemLS(),
},
},
Users: map[string]*webdav.User{},
},
}
if len(data.Users) == 0 {
log.Fatal("no user defined")
}
if len(data.Rules) != 0 {
config.webdav.User.Rules = parseRules(data.Rules)
}
parseUsers(data.Users, config)
return config
}
func basicAuth(c *cfg) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
username, password, authOK := r.BasicAuth()
if authOK == false {
http.Error(w, "Not authorized", 401)
return
}
p, ok := c.auth[username]
if !ok {
http.Error(w, "Not authorized", 401)
return
}
if password != p {
http.Error(w, "Not authorized", 401)
return
}
c.webdav.ServeHTTP(w, r)
})
}
func main() {
flag.Parse()
cfg := parseConfig()
handler := basicAuth(cfg)
// Builds the address and a listener.
laddr := ":" + cfg.port
listener, err := net.Listen("tcp", laddr)
if err != nil {
log.Fatal(err)
}
// Tell the user the port in which is listening.
fmt.Println("Listening on", listener.Addr().String())
// Starts the server.
if err := http.Serve(listener, handler); err != nil {
log.Fatal(err)
}
}