From 164f46188952d1cd714dfada07ef2e20d6dc4244 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Fri, 26 Jul 2024 09:38:38 +0200 Subject: [PATCH] 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'. --- README.md | 12 ++++++------ lib/config.go | 10 +++++----- lib/config_test.go | 28 ++++++++++++++-------------- lib/handler.go | 4 ++-- lib/permissions.go | 14 +++++++++++--- 5 files changed, 38 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 4ba949b..e4e3061 100644 --- a/README.md +++ b/README.md @@ -32,11 +32,11 @@ For usage information regarding the CLI, run `webdav --help`. ### 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 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. @@ -69,9 +69,9 @@ prefix: / debug: false # 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). -scope: . +directory: . # Whether the users can, by default, modify the contents. Default is false. modify: true @@ -84,10 +84,10 @@ users: # Example 'admin' user with plaintext password. - username: 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 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. - username: "{env}ENV_USERNAME" password: "{env}ENV_PASSWORD" diff --git a/lib/config.go b/lib/config.go index ebd4f87..5099791 100644 --- a/lib/config.go +++ b/lib/config.go @@ -13,7 +13,7 @@ import ( ) const ( - DefaultScope = "." + DefaultDirectory = "." DefaultModify = false DefaultDebug = false DefaultNoSniff = false @@ -74,7 +74,7 @@ func ParseConfig(filename string, flags *pflag.FlagSet) (*Config, error) { // empty or false. // Defaults shared with flags - v.SetDefault("Scope", DefaultScope) + v.SetDefault("Directory", DefaultDirectory) v.SetDefault("Modify", DefaultModify) v.SetDefault("Debug", DefaultDebug) v.SetDefault("NoSniff", DefaultNoSniff) @@ -111,8 +111,8 @@ func ParseConfig(filename string, flags *pflag.FlagSet) (*Config, error) { // Cascade user settings for i := range cfg.Users { - if !v.IsSet(fmt.Sprintf("Users.%d.Scope", i)) { - cfg.Users[i].Scope = cfg.Scope + if !v.IsSet(fmt.Sprintf("Users.%d.Directory", i)) { + cfg.Users[i].Directory = cfg.Directory } 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") } - c.Scope, err = filepath.Abs(c.Scope) + c.Directory, err = filepath.Abs(c.Directory) if err != nil { return fmt.Errorf("invalid config: %w", err) } diff --git a/lib/config_test.go b/lib/config_test.go index bd3b93c..7398f82 100644 --- a/lib/config_test.go +++ b/lib/config_test.go @@ -36,7 +36,7 @@ func TestConfigDefaults(t *testing.T) { dir, err := os.Getwd() 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.AllowedHosts) @@ -48,23 +48,23 @@ func TestConfigCascade(t *testing.T) { check := func(t *testing.T, cfg *Config) { 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.Users, 2) 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.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) } t.Run("YAML", func(t *testing.T) { content := ` -scope: / +directory: / modify: true rules: - path: /public/access/ @@ -75,7 +75,7 @@ users: password: admin - username: basic password: basic - scope: /basic + directory: /basic modify: false rules: []` @@ -87,7 +87,7 @@ users: t.Run("JSON", func(t *testing.T) { content := `{ - "scope": "/", + "directory": "/", "modify": true, "rules": [ { @@ -103,7 +103,7 @@ users: { "username": "basic", "password": "basic", - "scope": "/basic", + "directory": "/basic", "modify": false, "rules": [] } @@ -118,7 +118,7 @@ users: t.Run("`TOML", func(t *testing.T) { content := ` -scope = "/" +directory = "/" modify = true [[rules]] @@ -132,7 +132,7 @@ password = "admin" [[users]] username = "basic" password = "basic" -scope = "/basic" +directory = "/basic" modify = false rules = [] ` @@ -172,7 +172,7 @@ cors: func TestConfigRules(t *testing.T) { content := ` -scope: / +directory: / modify: true rules: - 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_DEBUG", "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) require.NoError(t, err) 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.Modify) @@ -212,5 +212,5 @@ func TestConfigEnv(t *testing.T) { require.NoError(t, os.Setenv("WD_PORT", "")) require.NoError(t, os.Setenv("WD_DEBUG", "")) require.NoError(t, os.Setenv("WD_MODIFY", "")) - require.NoError(t, os.Setenv("WD_SCOPE", "")) + require.NoError(t, os.Setenv("WD_DIRECTORY", "")) } diff --git a/lib/handler.go b/lib/handler.go index 5529123..f69005a 100644 --- a/lib/handler.go +++ b/lib/handler.go @@ -28,7 +28,7 @@ func NewHandler(c *Config) (http.Handler, error) { Handler: webdav.Handler{ Prefix: c.Prefix, FileSystem: Dir{ - Dir: webdav.Dir(c.Scope), + Dir: webdav.Dir(c.Directory), noSniff: c.NoSniff, }, LockSystem: webdav.NewMemLS(), @@ -43,7 +43,7 @@ func NewHandler(c *Config) (http.Handler, error) { Handler: webdav.Handler{ Prefix: c.Prefix, FileSystem: Dir{ - Dir: webdav.Dir(u.Scope), + Dir: webdav.Dir(u.Directory), noSniff: c.NoSniff, }, LockSystem: webdav.NewMemLS(), diff --git a/lib/permissions.go b/lib/permissions.go index 849fcf0..0276d08 100644 --- a/lib/permissions.go +++ b/lib/permissions.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "net/http" + "path/filepath" "regexp" "strings" ) @@ -40,9 +41,9 @@ func (r *Rule) Matches(path string) bool { } type Permissions struct { - Scope string - Modify bool - Rules []*Rule + Directory string + Modify bool + Rules []*Rule } // 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 { + 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 { if err := r.Validate(); err != nil { return fmt.Errorf("invalid permissions: %w", err)