From f70866490658c2a0ea239c68781df943422b6e16 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Fri, 26 Jul 2024 17:18:46 +0200 Subject: [PATCH] feat: permissions, auth, rules basic tests --- go.mod | 1 + go.sum | 2 + lib/handler_test.go | 251 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 254 insertions(+) create mode 100644 lib/handler_test.go diff --git a/go.mod b/go.mod index 1048ae2..1fdd53d 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.9.0 + github.com/studio-b12/gowebdav v0.9.0 go.uber.org/zap v1.27.0 golang.org/x/crypto v0.25.0 golang.org/x/net v0.27.0 diff --git a/go.sum b/go.sum index 3b7682a..dcd202b 100644 --- a/go.sum +++ b/go.sum @@ -56,6 +56,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/studio-b12/gowebdav v0.9.0 h1:1j1sc9gQnNxbXXM4M/CebPOX4aXYtr7MojAVcN4dHjU= +github.com/studio-b12/gowebdav v0.9.0/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= diff --git a/lib/handler_test.go b/lib/handler_test.go new file mode 100644 index 0000000..3c82610 --- /dev/null +++ b/lib/handler_test.go @@ -0,0 +1,251 @@ +package lib + +import ( + "fmt" + "net/http/httptest" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" + "github.com/studio-b12/gowebdav" +) + +func makeTestDirectory(t *testing.T, m map[string][]byte) string { + dir := t.TempDir() + + for path, data := range m { + filename := filepath.Join(dir, path) + + if data == nil { + err := os.MkdirAll(filename, 0775) + require.NoError(t, err) + } else { + err := os.MkdirAll(filepath.Dir(filename), 0775) + require.NoError(t, err) + + err = os.WriteFile(filename, data, 0664) + require.NoError(t, err) + } + } + + return dir +} + +func makeTestServer(t *testing.T, yamlConfig string) *httptest.Server { + cfg := writeAndParseConfig(t, yamlConfig, ".yml") + require.NoError(t, cfg.Validate()) + + handler, err := NewHandler(cfg) + require.NoError(t, err) + + return httptest.NewServer(handler) +} + +func TestServerDefaults(t *testing.T) { + t.Parallel() + + dir := makeTestDirectory(t, map[string][]byte{ + "foo.txt": []byte("foo"), + "sub/bar.txt": []byte("bar"), + }) + + srv := makeTestServer(t, "scope: "+dir) + client := gowebdav.NewClient(srv.URL, "", "") + + // By default, reading permissions. + files, err := client.ReadDir("/") + require.NoError(t, err) + require.Len(t, files, 2) + + data, err := client.Read("/foo.txt") + require.NoError(t, err) + require.EqualValues(t, []byte("foo"), data) + + files, err = client.ReadDir("/sub") + require.NoError(t, err) + require.Len(t, files, 1) + require.Equal(t, "bar.txt", files[0].Name()) + + data, err = client.Read("/sub/bar.txt") + require.NoError(t, err) + require.EqualValues(t, []byte("bar"), data) + + // By default, no modification permissions. + require.ErrorContains(t, client.Mkdir("/dir", 0666), "403") + require.ErrorContains(t, client.MkdirAll("/dir/path", 0666), "403") + require.ErrorContains(t, client.Remove("/foo.txt"), "403") + require.ErrorContains(t, client.RemoveAll("/foo.txt"), "403") + require.ErrorContains(t, client.Rename("/foo.txt", "/file2.txt", false), "403") + require.ErrorContains(t, client.Copy("/foo.txt", "/file2.txt", false), "403") + require.ErrorContains(t, client.Write("/foo.txt", []byte("hello world 2"), 0666), "403") +} + +func TestServerAuthentication(t *testing.T) { + t.Parallel() + + dir := makeTestDirectory(t, map[string][]byte{ + "foo.txt": []byte("foo"), + "sub/bar.txt": []byte("bar"), + }) + + srv := makeTestServer(t, fmt.Sprintf(` +auth: true +scope: %s +modify: true + +users: + - username: basic + password: basic + - username: bcrypt + password: "{bcrypt}$2a$12$222dfz8Nweoyvy8OwI8.me9nfaRfuz8lqGkiiYSMH1lLMHO26qWom" +`, dir)) + + t.Run("Basic Auth (Plaintext)", func(t *testing.T) { + t.Parallel() + + client := gowebdav.NewClient(srv.URL, "basic", "basic") + + files, err := client.ReadDir("/") + require.NoError(t, err) + require.Len(t, files, 2) + }) + + t.Run("Basic Auth (BCrypt)", func(t *testing.T) { + t.Parallel() + client := gowebdav.NewClient(srv.URL, "bcrypt", "bcrypt") + + files, err := client.ReadDir("/") + require.NoError(t, err) + require.Len(t, files, 2) + }) + + t.Run("Unauthorized (No Credentials)", func(t *testing.T) { + t.Parallel() + client := gowebdav.NewClient(srv.URL, "", "") + _, err := client.ReadDir("/") + require.ErrorContains(t, err, "401") + }) + + t.Run("Unauthorized (Wrong User)", func(t *testing.T) { + t.Parallel() + client := gowebdav.NewClient(srv.URL, "wrong", "basic") + _, err := client.ReadDir("/") + require.ErrorContains(t, err, "401") + }) + + t.Run("Unauthorized (Wrong Password)", func(t *testing.T) { + t.Parallel() + client := gowebdav.NewClient(srv.URL, "basic", "wrong") + _, err := client.ReadDir("/") + require.ErrorContains(t, err, "401") + }) +} + +func TestServerRules(t *testing.T) { + t.Parallel() + + dir := makeTestDirectory(t, map[string][]byte{ + "foo.txt": []byte("foo"), + "a/foo.js": []byte("foo js"), + "a/foo.txt": []byte("foo txt"), + "b/foo.txt": []byte("foo b"), + }) + + srv := makeTestServer(t, fmt.Sprintf(` +auth: true +scope: %s +modify: true + +users: + - username: basic + password: basic + rules: + - path: "^.+.js$" + regex: true + modify: false + - path: "/b" + modify: false +`, dir)) + + client := gowebdav.NewClient(srv.URL, "basic", "basic") + + files, err := client.ReadDir("/") + require.NoError(t, err) + require.Len(t, files, 3) + + err = client.Write("/foo.txt", []byte("new"), 0666) + require.NoError(t, err) + + err = client.Write("/a/foo.txt", []byte("new"), 0666) + require.NoError(t, err) + + err = client.Write("/a/foo.js", []byte("new"), 0666) + require.ErrorContains(t, err, "403") + + err = client.Write("/b/foo.txt", []byte("new"), 0666) + require.ErrorContains(t, err, "403") +} + +func TestServerPermissions(t *testing.T) { + t.Parallel() + + dir := makeTestDirectory(t, map[string][]byte{ + "foo.txt": []byte("foo"), + "a/foo.txt": []byte("foo a"), + "b/foo.txt": []byte("foo b"), + }) + + srv := makeTestServer(t, fmt.Sprintf(` +auth: true +scope: %s +modify: true + +users: + - username: a + password: a + scope: %s/a + - username: b + password: b + scope: %s/b + modify: false +`, dir, dir, dir)) + + t.Run("User A", func(t *testing.T) { + t.Parallel() + + client := gowebdav.NewClient(srv.URL, "a", "a") + + files, err := client.ReadDir("/") + require.NoError(t, err) + require.Len(t, files, 1) + + data, err := client.Read("/foo.txt") + require.NoError(t, err) + require.EqualValues(t, []byte("foo a"), data) + + err = client.Copy("/foo.txt", "/copy.txt", false) + require.NoError(t, err) + + data, err = client.Read("/copy.txt") + require.NoError(t, err) + require.EqualValues(t, []byte("foo a"), data) + }) + + t.Run("User B", func(t *testing.T) { + t.Parallel() + + client := gowebdav.NewClient(srv.URL, "b", "b") + + files, err := client.ReadDir("/") + require.NoError(t, err) + require.Len(t, files, 1) + + data, err := client.Read("/foo.txt") + require.NoError(t, err) + require.EqualValues(t, []byte("foo b"), data) + + err = client.Copy("/foo.txt", "/copy.txt", false) + require.ErrorContains(t, err, "403") + }) +}