mirror of
https://github.com/hacdias/webdav.git
synced 2026-09-26 05:01:54 +08:00
feat: allow no auth
License: MIT Signed-off-by: Henrique Dias <[email protected]>
This commit is contained in:
+10
-57
@@ -14,7 +14,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hacdias/webdav"
|
"github.com/hacdias/webdav"
|
||||||
"golang.org/x/crypto/bcrypt"
|
|
||||||
wd "golang.org/x/net/webdav"
|
wd "golang.org/x/net/webdav"
|
||||||
yaml "gopkg.in/yaml.v2"
|
yaml "gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
@@ -106,12 +105,12 @@ func parseUsers(raw []map[string]interface{}, c *cfg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.auth[username] = password
|
|
||||||
|
|
||||||
user := &webdav.User{
|
user := &webdav.User{
|
||||||
Scope: c.webdav.User.Scope,
|
Username: username,
|
||||||
Modify: c.webdav.User.Modify,
|
Password: password,
|
||||||
Rules: c.webdav.User.Rules,
|
Scope: c.webdav.User.Scope,
|
||||||
|
Modify: c.webdav.User.Modify,
|
||||||
|
Rules: c.webdav.User.Rules,
|
||||||
}
|
}
|
||||||
|
|
||||||
if scope, ok := r["scope"].(string); ok {
|
if scope, ok := r["scope"].(string); ok {
|
||||||
@@ -163,10 +162,8 @@ type cfg struct {
|
|||||||
address string
|
address string
|
||||||
port string
|
port string
|
||||||
tls bool
|
tls bool
|
||||||
noAuth bool
|
|
||||||
cert string
|
cert string
|
||||||
key string
|
key string
|
||||||
auth map[string]string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseConfig() *cfg {
|
func parseConfig() *cfg {
|
||||||
@@ -177,7 +174,7 @@ func parseConfig() *cfg {
|
|||||||
Port string `json:"port" yaml:"port"`
|
Port string `json:"port" yaml:"port"`
|
||||||
TLS bool `json:"tls" yaml:"tls"`
|
TLS bool `json:"tls" yaml:"tls"`
|
||||||
Cert string `json:"cert" yaml:"cert"`
|
Cert string `json:"cert" yaml:"cert"`
|
||||||
NoAuth bool `json:"noauth" yaml:"noauth"`
|
Auth bool `json:"auth" yaml:"auth"`
|
||||||
Key string `json:"key" yaml:"key"`
|
Key string `json:"key" yaml:"key"`
|
||||||
Scope string `json:"scope" yaml:"scope"`
|
Scope string `json:"scope" yaml:"scope"`
|
||||||
Modify bool `json:"modify" yaml:"modify"`
|
Modify bool `json:"modify" yaml:"modify"`
|
||||||
@@ -190,7 +187,7 @@ func parseConfig() *cfg {
|
|||||||
Cert: "cert.pem",
|
Cert: "cert.pem",
|
||||||
Key: "key.pem",
|
Key: "key.pem",
|
||||||
Scope: "./",
|
Scope: "./",
|
||||||
NoAuth: false,
|
Auth: true,
|
||||||
Modify: true,
|
Modify: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,8 +208,6 @@ func parseConfig() *cfg {
|
|||||||
tls: data.TLS,
|
tls: data.TLS,
|
||||||
cert: data.Cert,
|
cert: data.Cert,
|
||||||
key: data.Key,
|
key: data.Key,
|
||||||
noAuth: data.NoAuth,
|
|
||||||
auth: map[string]string{},
|
|
||||||
webdav: &webdav.Config{
|
webdav: &webdav.Config{
|
||||||
User: &webdav.User{
|
User: &webdav.User{
|
||||||
Scope: data.Scope,
|
Scope: data.Scope,
|
||||||
@@ -223,6 +218,7 @@ func parseConfig() *cfg {
|
|||||||
LockSystem: wd.NewMemLS(),
|
LockSystem: wd.NewMemLS(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Auth: data.Auth,
|
||||||
Users: map[string]*webdav.User{},
|
Users: map[string]*webdav.User{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -235,53 +231,10 @@ func parseConfig() *cfg {
|
|||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
func basicAuth(c *cfg) http.Handler {
|
|
||||||
if c.noAuth {
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
c.webdav.ServeHTTP(w, r)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
http.Error(w, "Not authorized", 401)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
p, ok := c.auth[username]
|
|
||||||
if !ok {
|
|
||||||
http.Error(w, "Not authorized", 401)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !checkPassword(p, password) {
|
|
||||||
log.Println("Wrong Password for user", username)
|
|
||||||
http.Error(w, "Not authorized", 401)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.webdav.ServeHTTP(w, r)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkPassword(saved, input string) bool {
|
|
||||||
if strings.HasPrefix(saved, "{bcrypt}") {
|
|
||||||
savedPassword := strings.TrimPrefix(saved, "{bcrypt}")
|
|
||||||
return bcrypt.CompareHashAndPassword([]byte(savedPassword), []byte(input)) == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return saved == input
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
cfg := parseConfig()
|
cfg := parseConfig()
|
||||||
|
|
||||||
handler := basicAuth(cfg)
|
|
||||||
|
|
||||||
// Builds the address and a listener.
|
// Builds the address and a listener.
|
||||||
laddr := cfg.address + ":" + cfg.port
|
laddr := cfg.address + ":" + cfg.port
|
||||||
listener, err := net.Listen("tcp", laddr)
|
listener, err := net.Listen("tcp", laddr)
|
||||||
@@ -294,11 +247,11 @@ func main() {
|
|||||||
|
|
||||||
// Starts the server.
|
// Starts the server.
|
||||||
if cfg.tls {
|
if cfg.tls {
|
||||||
if err := http.ServeTLS(listener, handler, cfg.cert, cfg.key); err != nil {
|
if err := http.ServeTLS(listener, cfg.webdav, cfg.cert, cfg.key); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := http.Serve(listener, handler); err != nil {
|
if err := http.Serve(listener, cfg.webdav); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
scope: .
|
||||||
|
address: 0.0.0.0
|
||||||
|
port: 8080
|
||||||
|
auth: false
|
||||||
|
users:
|
||||||
|
- username: admin
|
||||||
|
password: admin
|
||||||
|
- username: test
|
||||||
|
modify: false
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package webdav
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/net/webdav"
|
||||||
|
)
|
||||||
|
|
||||||
|
// User contains the settings of each user.
|
||||||
|
type User struct {
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
Scope string
|
||||||
|
Modify bool
|
||||||
|
Rules []*Rule
|
||||||
|
Handler *webdav.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allowed checks if the user has permission to access a directory/file
|
||||||
|
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
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package webdav
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkPassword(saved, input string) bool {
|
||||||
|
if strings.HasPrefix(saved, "{bcrypt}") {
|
||||||
|
savedPassword := strings.TrimPrefix(saved, "{bcrypt}")
|
||||||
|
return bcrypt.CompareHashAndPassword([]byte(savedPassword), []byte(input)) == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return saved == input
|
||||||
|
}
|
||||||
@@ -2,16 +2,15 @@ package webdav
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/net/webdav"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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
|
||||||
Users map[string]*User
|
Users map[string]*User
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,12 +18,29 @@ type Config struct {
|
|||||||
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
|
||||||
|
|
||||||
// Gets the correct user for this request.
|
if c.Auth {
|
||||||
username, _, ok := r.BasicAuth()
|
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
|
||||||
if ok {
|
|
||||||
if user, ok := c.Users[username]; ok {
|
// Gets the correct user for this request.
|
||||||
u = user
|
username, password, ok := r.BasicAuth()
|
||||||
|
if !ok {
|
||||||
|
http.Error(w, "Not authorized", 401)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
user, ok := c.Users[username]
|
||||||
|
if !ok {
|
||||||
|
http.Error(w, "Not authorized", 401)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !checkPassword(user.Password, password) {
|
||||||
|
log.Println("Wrong Password for user", username)
|
||||||
|
http.Error(w, "Not authorized", 401)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
u = user
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks for user permissions relatively to this PATH.
|
// Checks for user permissions relatively to this PATH.
|
||||||
@@ -76,36 +92,6 @@ type Rule struct {
|
|||||||
Regexp *regexp.Regexp
|
Regexp *regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
// User contains the settings of each user.
|
|
||||||
type User struct {
|
|
||||||
Scope string
|
|
||||||
Modify bool
|
|
||||||
Rules []*Rule
|
|
||||||
Handler *webdav.Handler
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allowed checks if the user has permission to access a directory/file
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// responseWriterNoBody is a wrapper used to suprress the body of the response
|
// responseWriterNoBody is a wrapper used to suprress the body of the response
|
||||||
// to a request. Mainly used for HEAD requests.
|
// to a request. Mainly used for HEAD requests.
|
||||||
type responseWriterNoBody struct {
|
type responseWriterNoBody struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user