Compare commits

...
10 Commits
Author SHA1 Message Date
Henrique Dias ab0334f036 chore: add mips(64)(le)
License: MIT
Signed-off-by: Henrique Dias <[email protected]>
2019-07-14 09:57:31 +01:00
Henrique Dias 931f125224 chore: go mod tidy
License: MIT
Signed-off-by: Henrique Dias <[email protected]>
2019-06-11 13:12:22 +01:00
Henrique Dias cd472b26be chore: bump to v2.0.0
License: MIT
Signed-off-by: Henrique Dias <[email protected]>
2019-06-11 13:09:51 +01:00
Steven Vandevelde 7358553e69 feat(BREAKING): extend cors functionality (#25) 2019-06-11 13:02:10 +01:00
Henrique Dias 764a69cd33 fix: numeric passwords (#24)
License: MIT
Signed-off-by: Henrique Dias <[email protected]>
2019-06-09 13:51:53 +01:00
Henrique Dias d266f1150e fix: auth enabled by default
License: MIT
Signed-off-by: Henrique Dias <[email protected]>
2019-06-09 13:47:28 +01:00
Henrique Dias 76ebaffaef docs: add cors config
License: MIT
Signed-off-by: Henrique Dias <[email protected]>
2019-05-24 14:49:01 +01:00
Henrique Dias 60f2697615 fix: pass through linters
License: MIT
Signed-off-by: Henrique Dias <[email protected]>
2019-05-24 14:47:05 +01:00
oskar e5b3946388 feat: add support for custom CORS headers 2019-05-24 14:43:22 +01:00
Henrique Dias 8c66f0c585 feat: check basic auth user anyways
License: MIT
Signed-off-by: Henrique Dias <[email protected]>
2019-05-12 20:25:36 +01:00
10 changed files with 153 additions and 5 deletions
+4
View File
@@ -22,6 +22,10 @@ build:
- 386 - 386
- arm - arm
- arm64 - arm64
- mips
- mipsle
- mips64
- mips64le
goarm: goarm:
- 5 - 5
- 6 - 6
+21
View File
@@ -26,6 +26,20 @@ scope: .
modify: true modify: true
rules: [] rules: []
# CORS configuration
cors:
enabled: true
credentials: true
allowed_headers:
- Depth
allowed_hosts:
- http://localhost:8080
allowed_methods:
- GET
exposed_headers:
- Content-Length
- Content-Range
users: users:
- username: admin - username: admin
password: admin password: admin
@@ -49,6 +63,13 @@ There are more ways to customize how you run WebDAV through flags and environmen
An example of how to use this with `systemd` is on [webdav.service.example](/webdav.service.example). An example of how to use this with `systemd` is on [webdav.service.example](/webdav.service.example).
### CORS
The `allowed_*` properties are optional, the default value for each of them will be `*`. `exposed_headers` is optional as well, but is not set if not defined. Setting `credentials` to `true` will allow you to:
1. Use `withCredentials = true` in javascript.
2. Use the `username:password@host` syntax.
## License ## License
MIT © [Henrique Dias](https://hacdias.com) MIT © [Henrique Dias](https://hacdias.com)
+1 -1
View File
@@ -9,4 +9,4 @@ func Execute() {
if err := rootCmd.Execute(); err != nil { if err := rootCmd.Execute(); err != nil {
log.Fatal(err) log.Fatal(err)
} }
} }
+56 -2
View File
@@ -5,9 +5,10 @@ import (
"log" "log"
"os" "os"
"regexp" "regexp"
"strconv"
"strings" "strings"
"github.com/hacdias/webdav/webdav" "github.com/hacdias/webdav/v2/webdav"
"github.com/spf13/pflag" "github.com/spf13/pflag"
v "github.com/spf13/viper" v "github.com/spf13/viper"
wd "golang.org/x/net/webdav" wd "golang.org/x/net/webdav"
@@ -81,6 +82,10 @@ func parseUsers(raw []interface{}, c *webdav.Config) {
password, ok := u["password"].(string) password, ok := u["password"].(string)
if !ok { if !ok {
password = "" password = ""
if numPwd, ok := u["password"].(int); ok {
password = strconv.Itoa(numPwd)
}
} }
if strings.HasPrefix(password, "{env}") { if strings.HasPrefix(password, "{env}") {
@@ -118,6 +123,46 @@ func parseUsers(raw []interface{}, c *webdav.Config) {
} }
} }
func parseCors(cfg map[string]interface{}, c *webdav.Config) {
cors := webdav.CorsCfg{
Enabled: cfg["enabled"].(bool),
Credentials: cfg["credentials"].(bool),
}
cors.AllowedHeaders = corsProperty("allowed_headers", cfg)
cors.AllowedHosts = corsProperty("allowed_hosts", cfg)
cors.AllowedMethods = corsProperty("allowed_methods", cfg)
cors.ExposedHeaders = corsProperty("exposed_headers", cfg)
c.Cors = cors
}
func corsProperty(property string, cfg map[string]interface{}) []string {
var def []string
if property == "exposed_headers" {
def = []string{}
} else {
def = []string{"*"}
}
if allowed, ok := cfg[property].([]interface{}); ok {
items := make([]string, len(allowed))
for idx, a := range allowed {
items[idx] = a.(string)
}
if len(items) == 0 {
return def
} else {
return items
}
}
return def
}
func readConfig(flags *pflag.FlagSet) *webdav.Config { func readConfig(flags *pflag.FlagSet) *webdav.Config {
cfg := &webdav.Config{ cfg := &webdav.Config{
User: &webdav.User{ User: &webdav.User{
@@ -129,7 +174,11 @@ func readConfig(flags *pflag.FlagSet) *webdav.Config {
LockSystem: wd.NewMemLS(), LockSystem: wd.NewMemLS(),
}, },
}, },
Auth: getOptB(flags, "auth"), Auth: getOptB(flags, "auth"),
Cors: webdav.CorsCfg{
Enabled: false,
Credentials: false,
},
Users: map[string]*webdav.User{}, Users: map[string]*webdav.User{},
} }
@@ -143,6 +192,11 @@ func readConfig(flags *pflag.FlagSet) *webdav.Config {
parseUsers(users, cfg) parseUsers(users, cfg)
} }
rawCors := v.Get("cors")
if cors, ok := rawCors.(map[string]interface{}); ok {
parseCors(cors, cfg)
}
if len(cfg.Users) != 0 && !cfg.Auth { if len(cfg.Users) != 0 && !cfg.Auth {
log.Print("Users will be ignored due to auth=false") log.Print("Users will be ignored due to auth=false")
} }
+1
View File
@@ -21,6 +21,7 @@ func init() {
flags := rootCmd.Flags() flags := rootCmd.Flags()
flags.StringVarP(&cfgFile, "config", "c", "", "config file path") flags.StringVarP(&cfgFile, "config", "c", "", "config file path")
flags.BoolP("tls", "t", false, "enable tls") flags.BoolP("tls", "t", false, "enable tls")
flags.Bool("auth", true, "enable auth")
flags.String("cert", "cert.pem", "TLS certificate") flags.String("cert", "cert.pem", "TLS certificate")
flags.String("key", "key.pem", "TLS key") flags.String("key", "key.pem", "TLS key")
flags.StringP("address", "a", "0.0.0.0", "address to listen to") flags.StringP("address", "a", "0.0.0.0", "address to listen to")
+1 -1
View File
@@ -1,4 +1,4 @@
module github.com/hacdias/webdav module github.com/hacdias/webdav/v2
go 1.12 go 1.12
+1 -1
View File
@@ -3,7 +3,7 @@ package main
import ( import (
"runtime" "runtime"
"github.com/hacdias/webdav/cmd" "github.com/hacdias/webdav/v2/cmd"
) )
func main() { func main() {
Regular → Executable
View File
Regular → Executable
+9
View File
@@ -14,3 +14,12 @@ func checkPassword(saved, input string) bool {
return saved == input return saved == input
} }
func isAllowedHost(allowedHosts []string, origin string) bool {
for _, host := range allowedHosts {
if host == origin {
return true
}
}
return false
}
Regular → Executable
+59
View File
@@ -4,19 +4,68 @@ import (
"context" "context"
"log" "log"
"net/http" "net/http"
"strings"
) )
// CorsCfg is the CORS config.
type CorsCfg struct {
Enabled bool
Credentials bool
AllowedHeaders []string
AllowedHosts []string
AllowedMethods []string
ExposedHeaders []string
}
// Config is the configuration of a WebDAV instance. // Config is the configuration of a WebDAV instance.
type Config struct { type Config struct {
*User *User
Auth bool Auth bool
Cors CorsCfg
Users map[string]*User Users map[string]*User
} }
// ServeHTTP determines if the request is for this plugin, and if all prerequisites are met. // ServeHTTP determines if the request is for this plugin, and if all prerequisites are met.
func (c *Config) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (c *Config) ServeHTTP(w http.ResponseWriter, r *http.Request) {
u := c.User u := c.User
requestOrigin := r.Header.Get("Origin")
// Add CORS headers before any operation so even on a 401 unauthorized status, CORS will work.
if c.Cors.Enabled && requestOrigin != "" {
headers := w.Header()
allowedHeaders := strings.Join(c.Cors.AllowedHeaders, ", ")
allowedMethods := strings.Join(c.Cors.AllowedMethods, ", ")
exposedHeaders := strings.Join(c.Cors.ExposedHeaders, ", ")
allowAllHosts := len(c.Cors.AllowedHosts) == 1 && c.Cors.AllowedHosts[0] == "*"
allowedHost := isAllowedHost(c.Cors.AllowedHosts, requestOrigin)
if allowAllHosts {
headers.Set("Access-Control-Allow-Origin", "*")
} else if allowedHost {
headers.Set("Access-Control-Allow-Origin", requestOrigin)
}
if allowAllHosts || allowedHost {
headers.Set("Access-Control-Allow-Headers", allowedHeaders)
headers.Set("Access-Control-Allow-Methods", allowedMethods)
if c.Cors.Credentials {
headers.Set("Access-Control-Allow-Credentials", "true")
}
if len(c.Cors.ExposedHeaders) > 0 {
headers.Set("Access-Control-Expose-Headers", exposedHeaders)
}
}
}
if r.Method == "OPTIONS" && c.Cors.Enabled && requestOrigin != "" {
return
}
// Authentication
if c.Auth { if c.Auth {
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`) w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
@@ -40,6 +89,16 @@ func (c *Config) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
u = user u = user
} else {
// Even if Auth is disabled, we might want to get
// the user from the Basic Auth header. Useful for Caddy
// plugin implementation.
username, _, ok := r.BasicAuth()
if ok {
if user, ok := c.Users[username]; ok {
u = user
}
}
} }
// Checks for user permissions relatively to this PATH. // Checks for user permissions relatively to this PATH.