feat!: rename 'scope' to 'directory'

Directory makes it more clear of what it is. In addition, this will make it easier when
allowing for multiple directories in the future, since we can just name it 'directories', which is more clear than 'scopes'.
This commit is contained in:
Henrique Dias
2024-07-29 10:11:02 +02:00
parent a255fb51e2
commit d5e5052f63
6 changed files with 44 additions and 36 deletions
+6 -6
View File
@@ -34,11 +34,11 @@ For usage information regarding the CLI, run `webdav --help`.
### Docker ### Docker
To use with Docker, you need to provide a configuration file and mount the data directories. For example, let's take the following configuration file that simply sets the port to `6060` and the scope to `/data`. To use with Docker, you need to provide a configuration file and mount the data directories. For example, let's take the following configuration file that simply sets the port to `6060` and the directory to `/data`.
```yaml ```yaml
port: 6060 port: 6060
scope: /data directory: /data
``` ```
You can now run with the following Docker command, where you mount the configuration file inside the container, and the data directory too, as well as forwarding the port 6060. You will need to change this to match your own configuration. You can now run with the following Docker command, where you mount the configuration file inside the container, and the data directory too, as well as forwarding the port 6060. You will need to change this to match your own configuration.
@@ -71,9 +71,9 @@ prefix: /
debug: false debug: 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 'scope' defined. # This directory will be used by users unless they have their own 'directory' defined.
# Default is "." (current directory). # Default is "." (current directory).
scope: . directory: .
# Whether the users can, by default, modify the contents. Default is false. # Whether the users can, by default, modify the contents. Default is false.
modify: true modify: true
@@ -86,10 +86,10 @@ users:
# Example 'admin' user with plaintext password. # Example 'admin' user with plaintext password.
- username: admin - username: admin
password: admin password: admin
# Example 'john' user with bcrypt encrypted password, with custom scope. # Example 'john' user with bcrypt encrypted password, with custom directory.
- username: john - username: john
password: "{bcrypt}$2y$10$zEP6oofmXFeHaeMfBNLnP.DO8m.H.Mwhd24/TOX2MWLxAExXi4qgi" password: "{bcrypt}$2y$10$zEP6oofmXFeHaeMfBNLnP.DO8m.H.Mwhd24/TOX2MWLxAExXi4qgi"
scope: /another/path directory: /another/path
# Example user whose details will be picked up from the environment. # Example user whose details will be picked up from the environment.
- username: "{env}ENV_USERNAME" - username: "{env}ENV_USERNAME"
password: "{env}ENV_PASSWORD" password: "{env}ENV_PASSWORD"
+5 -5
View File
@@ -13,7 +13,7 @@ import (
) )
const ( const (
DefaultScope = "." DefaultDirectory = "."
DefaultModify = false DefaultModify = false
DefaultDebug = false DefaultDebug = false
DefaultNoSniff = false DefaultNoSniff = false
@@ -74,7 +74,7 @@ func ParseConfig(filename string, flags *pflag.FlagSet) (*Config, error) {
// empty or false. // empty or false.
// Defaults shared with flags // Defaults shared with flags
v.SetDefault("Scope", DefaultScope) v.SetDefault("Directory", DefaultDirectory)
v.SetDefault("Modify", DefaultModify) v.SetDefault("Modify", DefaultModify)
v.SetDefault("Debug", DefaultDebug) v.SetDefault("Debug", DefaultDebug)
v.SetDefault("NoSniff", DefaultNoSniff) v.SetDefault("NoSniff", DefaultNoSniff)
@@ -111,8 +111,8 @@ func ParseConfig(filename string, flags *pflag.FlagSet) (*Config, error) {
// Cascade user settings // Cascade user settings
for i := range cfg.Users { for i := range cfg.Users {
if !v.IsSet(fmt.Sprintf("Users.%d.Scope", i)) { if !v.IsSet(fmt.Sprintf("Users.%d.Directory", i)) {
cfg.Users[i].Scope = cfg.Scope cfg.Users[i].Directory = cfg.Directory
} }
if !v.IsSet(fmt.Sprintf("Users.%d.Modify", i)) { if !v.IsSet(fmt.Sprintf("Users.%d.Modify", i)) {
@@ -139,7 +139,7 @@ func (c *Config) Validate() error {
zap.L().Warn("unprotected config: no users have been set, so no authentication will be used") zap.L().Warn("unprotected config: no users have been set, so no authentication will be used")
} }
c.Scope, err = filepath.Abs(c.Scope) c.Directory, err = filepath.Abs(c.Directory)
if err != nil { if err != nil {
return fmt.Errorf("invalid config: %w", err) return fmt.Errorf("invalid config: %w", err)
} }
+14 -14
View File
@@ -36,7 +36,7 @@ func TestConfigDefaults(t *testing.T) {
dir, err := os.Getwd() dir, err := os.Getwd()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, dir, cfg.Scope) require.Equal(t, dir, cfg.Directory)
require.EqualValues(t, []string{"*"}, cfg.CORS.AllowedHeaders) require.EqualValues(t, []string{"*"}, cfg.CORS.AllowedHeaders)
require.EqualValues(t, []string{"*"}, cfg.CORS.AllowedHosts) require.EqualValues(t, []string{"*"}, cfg.CORS.AllowedHosts)
@@ -48,23 +48,23 @@ func TestConfigCascade(t *testing.T) {
check := func(t *testing.T, cfg *Config) { check := func(t *testing.T, cfg *Config) {
require.True(t, cfg.Modify) require.True(t, cfg.Modify)
require.Equal(t, "/", cfg.Scope) require.Equal(t, "/", cfg.Directory)
require.Len(t, cfg.Rules, 1) require.Len(t, cfg.Rules, 1)
require.Len(t, cfg.Users, 2) require.Len(t, cfg.Users, 2)
require.True(t, cfg.Users[0].Modify) require.True(t, cfg.Users[0].Modify)
require.Equal(t, "/", cfg.Users[0].Scope) require.Equal(t, "/", cfg.Users[0].Directory)
require.Len(t, cfg.Users[0].Rules, 1) require.Len(t, cfg.Users[0].Rules, 1)
require.False(t, cfg.Users[1].Modify) require.False(t, cfg.Users[1].Modify)
require.Equal(t, "/basic", cfg.Users[1].Scope) require.Equal(t, "/basic", cfg.Users[1].Directory)
require.Len(t, cfg.Users[1].Rules, 0) require.Len(t, cfg.Users[1].Rules, 0)
} }
t.Run("YAML", func(t *testing.T) { t.Run("YAML", func(t *testing.T) {
content := ` content := `
scope: / directory: /
modify: true modify: true
rules: rules:
- path: /public/access/ - path: /public/access/
@@ -75,7 +75,7 @@ users:
password: admin password: admin
- username: basic - username: basic
password: basic password: basic
scope: /basic directory: /basic
modify: false modify: false
rules: []` rules: []`
@@ -87,7 +87,7 @@ users:
t.Run("JSON", func(t *testing.T) { t.Run("JSON", func(t *testing.T) {
content := `{ content := `{
"scope": "/", "directory": "/",
"modify": true, "modify": true,
"rules": [ "rules": [
{ {
@@ -103,7 +103,7 @@ users:
{ {
"username": "basic", "username": "basic",
"password": "basic", "password": "basic",
"scope": "/basic", "directory": "/basic",
"modify": false, "modify": false,
"rules": [] "rules": []
} }
@@ -118,7 +118,7 @@ users:
t.Run("`TOML", func(t *testing.T) { t.Run("`TOML", func(t *testing.T) {
content := ` content := `
scope = "/" directory = "/"
modify = true modify = true
[[rules]] [[rules]]
@@ -132,7 +132,7 @@ password = "admin"
[[users]] [[users]]
username = "basic" username = "basic"
password = "basic" password = "basic"
scope = "/basic" directory = "/basic"
modify = false modify = false
rules = [] rules = []
` `
@@ -172,7 +172,7 @@ cors:
func TestConfigRules(t *testing.T) { func TestConfigRules(t *testing.T) {
content := ` content := `
scope: / directory: /
modify: true modify: true
rules: rules:
- regex: '^.+\.js$' - regex: '^.+\.js$'
@@ -198,13 +198,13 @@ func TestConfigEnv(t *testing.T) {
require.NoError(t, os.Setenv("WD_PORT", "1234")) require.NoError(t, os.Setenv("WD_PORT", "1234"))
require.NoError(t, os.Setenv("WD_DEBUG", "true")) require.NoError(t, os.Setenv("WD_DEBUG", "true"))
require.NoError(t, os.Setenv("WD_MODIFY", "true")) require.NoError(t, os.Setenv("WD_MODIFY", "true"))
require.NoError(t, os.Setenv("WD_SCOPE", "/test")) require.NoError(t, os.Setenv("WD_DIRECTORY", "/test"))
cfg, err := ParseConfig("", nil) cfg, err := ParseConfig("", nil)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, 1234, cfg.Port) assert.Equal(t, 1234, cfg.Port)
assert.Equal(t, "/test", cfg.Scope) assert.Equal(t, "/test", cfg.Directory)
assert.Equal(t, true, cfg.Debug) assert.Equal(t, true, cfg.Debug)
assert.Equal(t, true, cfg.Modify) assert.Equal(t, true, cfg.Modify)
@@ -212,5 +212,5 @@ func TestConfigEnv(t *testing.T) {
require.NoError(t, os.Setenv("WD_PORT", "")) require.NoError(t, os.Setenv("WD_PORT", ""))
require.NoError(t, os.Setenv("WD_DEBUG", "")) require.NoError(t, os.Setenv("WD_DEBUG", ""))
require.NoError(t, os.Setenv("WD_MODIFY", "")) require.NoError(t, os.Setenv("WD_MODIFY", ""))
require.NoError(t, os.Setenv("WD_SCOPE", "")) require.NoError(t, os.Setenv("WD_DIRECTORY", ""))
} }
+2 -2
View File
@@ -28,7 +28,7 @@ func NewHandler(c *Config) (http.Handler, error) {
Handler: webdav.Handler{ Handler: webdav.Handler{
Prefix: c.Prefix, Prefix: c.Prefix,
FileSystem: Dir{ FileSystem: Dir{
Dir: webdav.Dir(c.Scope), Dir: webdav.Dir(c.Directory),
noSniff: c.NoSniff, noSniff: c.NoSniff,
}, },
LockSystem: webdav.NewMemLS(), LockSystem: webdav.NewMemLS(),
@@ -43,7 +43,7 @@ func NewHandler(c *Config) (http.Handler, error) {
Handler: webdav.Handler{ Handler: webdav.Handler{
Prefix: c.Prefix, Prefix: c.Prefix,
FileSystem: Dir{ FileSystem: Dir{
Dir: webdav.Dir(u.Scope), Dir: webdav.Dir(u.Directory),
noSniff: c.NoSniff, noSniff: c.NoSniff,
}, },
LockSystem: webdav.NewMemLS(), LockSystem: webdav.NewMemLS(),
+6 -6
View File
@@ -50,7 +50,7 @@ func TestServerDefaults(t *testing.T) {
"sub/bar.txt": []byte("bar"), "sub/bar.txt": []byte("bar"),
}) })
srv := makeTestServer(t, "scope: "+dir) srv := makeTestServer(t, "directory: "+dir)
client := gowebdav.NewClient(srv.URL, "", "") client := gowebdav.NewClient(srv.URL, "", "")
// By default, reading permissions. // By default, reading permissions.
@@ -90,7 +90,7 @@ func TestServerAuthentication(t *testing.T) {
}) })
srv := makeTestServer(t, fmt.Sprintf(` srv := makeTestServer(t, fmt.Sprintf(`
scope: %s directory: %s
modify: true modify: true
users: users:
@@ -152,7 +152,7 @@ func TestServerRules(t *testing.T) {
}) })
srv := makeTestServer(t, fmt.Sprintf(` srv := makeTestServer(t, fmt.Sprintf(`
scope: %s directory: %s
modify: true modify: true
users: users:
@@ -194,16 +194,16 @@ func TestServerPermissions(t *testing.T) {
}) })
srv := makeTestServer(t, fmt.Sprintf(` srv := makeTestServer(t, fmt.Sprintf(`
scope: %s directory: %s
modify: true modify: true
users: users:
- username: a - username: a
password: a password: a
scope: %s/a directory: %s/a
- username: b - username: b
password: b password: b
scope: %s/b directory: %s/b
modify: false modify: false
`, dir, dir, dir)) `, dir, dir, dir))
+11 -3
View File
@@ -4,6 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"path/filepath"
"regexp" "regexp"
"strings" "strings"
) )
@@ -40,9 +41,9 @@ func (r *Rule) Matches(path string) bool {
} }
type Permissions struct { type Permissions struct {
Scope string Directory string
Modify bool Modify bool
Rules []*Rule Rules []*Rule
} }
// Allowed checks if the user has permission to access a directory/file // Allowed checks if the user has permission to access a directory/file
@@ -69,6 +70,13 @@ func (p Permissions) Allowed(r *http.Request) bool {
} }
func (p *Permissions) Validate() error { func (p *Permissions) Validate() error {
var err error
p.Directory, err = filepath.Abs(p.Directory)
if err != nil {
return fmt.Errorf("invalid permissions: %w", err)
}
for _, r := range p.Rules { for _, r := range p.Rules {
if err := r.Validate(); err != nil { if err := r.Validate(); err != nil {
return fmt.Errorf("invalid permissions: %w", err) return fmt.Errorf("invalid permissions: %w", err)