mirror of
https://github.com/hacdias/webdav.git
synced 2026-09-22 03:20:41 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d418bd2661 | ||
|
|
d500716f29 | ||
|
|
8c49af0b68 |
@@ -71,6 +71,11 @@ debug: false
|
|||||||
# Disable sniffing the files to detect their content type. Default is 'false'.
|
# Disable sniffing the files to detect their content type. Default is 'false'.
|
||||||
noSniff: false
|
noSniff: false
|
||||||
|
|
||||||
|
# Whether the server runs behind a trusted proxy or not. When this is true,
|
||||||
|
# the header X-Forwarded-For will be used for logging the remote addresses
|
||||||
|
# of logging attempts (if available).
|
||||||
|
behindProxy: false
|
||||||
|
|
||||||
# The directory that will be able to be accessed by the users when connecting.
|
# The directory that will be able to be accessed by the users when connecting.
|
||||||
# This directory will be used by users unless they have their own 'directory' defined.
|
# This directory will be used by users unless they have their own 'directory' defined.
|
||||||
# Default is '.' (current directory).
|
# Default is '.' (current directory).
|
||||||
|
|||||||
+3
-2
@@ -33,6 +33,7 @@ type Config struct {
|
|||||||
Prefix string
|
Prefix string
|
||||||
NoSniff bool
|
NoSniff bool
|
||||||
NoPassword bool
|
NoPassword bool
|
||||||
|
BehindProxy bool
|
||||||
Log Log
|
Log Log
|
||||||
CORS CORS
|
CORS CORS
|
||||||
Users []User
|
Users []User
|
||||||
@@ -160,8 +161,8 @@ func (c *Config) Validate() error {
|
|||||||
return fmt.Errorf("invalid config: %w", err)
|
return fmt.Errorf("invalid config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, u := range c.Users {
|
for i := range c.Users {
|
||||||
err := u.Validate(c.NoPassword)
|
err := c.Users[i].Validate(c.NoPassword)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid config: %w", err)
|
return fmt.Errorf("invalid config: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,17 @@ func writeAndParseConfig(t *testing.T, content, extension string) *Config {
|
|||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writeAndParseConfigWithError(t *testing.T, content, extension, error string) {
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
tmpFile := filepath.Join(tmpDir, "config"+extension)
|
||||||
|
|
||||||
|
err := os.WriteFile(tmpFile, []byte(content), 0666)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = ParseConfig(tmpFile, nil)
|
||||||
|
require.ErrorContains(t, err, error)
|
||||||
|
}
|
||||||
|
|
||||||
func TestConfigDefaults(t *testing.T) {
|
func TestConfigDefaults(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -224,3 +235,33 @@ func TestConfigEnv(t *testing.T) {
|
|||||||
require.NoError(t, os.Setenv("WD_PERMISSIONS", ""))
|
require.NoError(t, os.Setenv("WD_PERMISSIONS", ""))
|
||||||
require.NoError(t, os.Setenv("WD_DIRECTORY", ""))
|
require.NoError(t, os.Setenv("WD_DIRECTORY", ""))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConfigParseUserPasswordEnvironment(t *testing.T) {
|
||||||
|
content := `
|
||||||
|
directory: /
|
||||||
|
users:
|
||||||
|
- username: '{env}USER1_USERNAME'
|
||||||
|
password: '{env}USER1_PASSWORD'
|
||||||
|
- username: basic
|
||||||
|
password: basic
|
||||||
|
`
|
||||||
|
|
||||||
|
writeAndParseConfigWithError(t, content, ".yml", "username environment variable is empty")
|
||||||
|
|
||||||
|
err := os.Setenv("USER1_USERNAME", "admin")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
writeAndParseConfigWithError(t, content, ".yml", "password environment variable is empty")
|
||||||
|
|
||||||
|
err = os.Setenv("USER1_PASSWORD", "admin")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
cfg := writeAndParseConfig(t, content, ".yaml")
|
||||||
|
require.NoError(t, cfg.Validate())
|
||||||
|
|
||||||
|
require.Equal(t, "admin", cfg.Users[0].Username)
|
||||||
|
require.Equal(t, "basic", cfg.Users[1].Username)
|
||||||
|
|
||||||
|
require.True(t, cfg.Users[0].checkPassword("admin"))
|
||||||
|
require.True(t, cfg.Users[1].checkPassword("basic"))
|
||||||
|
}
|
||||||
|
|||||||
+15
-11
@@ -17,14 +17,16 @@ type handlerUser struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
noPassword bool
|
noPassword bool
|
||||||
user *handlerUser
|
behindProxy bool
|
||||||
users map[string]*handlerUser
|
user *handlerUser
|
||||||
|
users map[string]*handlerUser
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHandler(c *Config) (http.Handler, error) {
|
func NewHandler(c *Config) (http.Handler, error) {
|
||||||
h := &Handler{
|
h := &Handler{
|
||||||
noPassword: c.NoPassword,
|
noPassword: c.NoPassword,
|
||||||
|
behindProxy: c.BehindProxy,
|
||||||
user: &handlerUser{
|
user: &handlerUser{
|
||||||
User: User{
|
User: User{
|
||||||
UserPermissions: c.UserPermissions,
|
UserPermissions: c.UserPermissions,
|
||||||
@@ -61,6 +63,7 @@ func NewHandler(c *Config) (http.Handler, error) {
|
|||||||
AllowedOrigins: c.CORS.AllowedHosts,
|
AllowedOrigins: c.CORS.AllowedHosts,
|
||||||
AllowedMethods: c.CORS.AllowedMethods,
|
AllowedMethods: c.CORS.AllowedMethods,
|
||||||
AllowedHeaders: c.CORS.AllowedHeaders,
|
AllowedHeaders: c.CORS.AllowedHeaders,
|
||||||
|
ExposedHeaders: c.CORS.ExposedHeaders,
|
||||||
OptionsPassthrough: false,
|
OptionsPassthrough: false,
|
||||||
}).Handler(h), nil
|
}).Handler(h), nil
|
||||||
}
|
}
|
||||||
@@ -85,7 +88,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
|
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
|
||||||
|
|
||||||
// Retrieve the real client IP address using the updated helper function
|
// Retrieve the real client IP address using the updated helper function
|
||||||
remoteAddr := getRealRemoteIP(r)
|
remoteAddr := getRealRemoteIP(r, h.behindProxy)
|
||||||
|
|
||||||
// Gets the correct user for this request.
|
// Gets the correct user for this request.
|
||||||
username, password, ok := r.BasicAuth()
|
username, password, ok := r.BasicAuth()
|
||||||
@@ -166,12 +169,13 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getRealRemoteIP retrieves the client's actual IP address, considering reverse proxies.
|
// getRealRemoteIP retrieves the client's actual IP address, considering reverse proxies.
|
||||||
func getRealRemoteIP(r *http.Request) string {
|
func getRealRemoteIP(r *http.Request, behindProxy bool) string {
|
||||||
ip := r.Header.Get("X-Forwarded-For")
|
if behindProxy {
|
||||||
if ip == "" {
|
if ip := r.Header.Get("X-Forwarded-For"); ip != "" {
|
||||||
ip = r.RemoteAddr
|
return ip
|
||||||
}
|
}
|
||||||
return ip
|
}
|
||||||
|
return r.RemoteAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
type responseWriterNoBody struct {
|
type responseWriterNoBody struct {
|
||||||
|
|||||||
+10
-1
@@ -27,12 +27,21 @@ func (u User) checkPassword(input string) bool {
|
|||||||
func (u *User) Validate(noPassword bool) error {
|
func (u *User) Validate(noPassword bool) error {
|
||||||
if u.Username == "" {
|
if u.Username == "" {
|
||||||
return errors.New("invalid user: username must be set")
|
return errors.New("invalid user: username must be set")
|
||||||
|
} else if strings.HasPrefix(u.Username, "{env}") {
|
||||||
|
env := strings.TrimPrefix(u.Username, "{env}")
|
||||||
|
if env == "" {
|
||||||
|
return fmt.Errorf("invalid user %q: username environment variable not set", u.Username)
|
||||||
|
}
|
||||||
|
|
||||||
|
u.Username = os.Getenv(env)
|
||||||
|
if u.Username == "" {
|
||||||
|
return fmt.Errorf("invalid user %q: username environment variable is empty", u.Username)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.Password == "" && !noPassword {
|
if u.Password == "" && !noPassword {
|
||||||
return fmt.Errorf("invalid user %q: password must be set", u.Username)
|
return fmt.Errorf("invalid user %q: password must be set", u.Username)
|
||||||
} else if strings.HasPrefix(u.Password, "{env}") {
|
} else if strings.HasPrefix(u.Password, "{env}") {
|
||||||
|
|
||||||
env := strings.TrimPrefix(u.Password, "{env}")
|
env := strings.TrimPrefix(u.Password, "{env}")
|
||||||
if env == "" {
|
if env == "" {
|
||||||
return fmt.Errorf("invalid user %q: password environment variable not set", u.Username)
|
return fmt.Errorf("invalid user %q: password environment variable not set", u.Username)
|
||||||
|
|||||||
Reference in New Issue
Block a user