feat: allow no auth

License: MIT
Signed-off-by: Henrique Dias <[email protected]>
This commit is contained in:
Henrique Dias
2019-05-12 16:29:36 +01:00
parent 35fd913321
commit 5239649127
5 changed files with 98 additions and 95 deletions
+10 -57
View File
@@ -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)
} }
+9
View File
@@ -0,0 +1,9 @@
scope: .
address: 0.0.0.0
port: 8080
auth: false
users:
- username: admin
password: admin
- username: test
modify: false
+39
View File
@@ -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
}
+16
View File
@@ -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
}
+24 -38
View File
@@ -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 {