aboutsummaryrefslogtreecommitdiff
path: root/src/zenserver-test/zenserver-test.cpp
diff options
context:
space:
mode:
authorDan Engelbrecht <[email protected]>2024-10-23 10:31:43 +0200
committerGitHub Enterprise <[email protected]>2024-10-23 10:31:43 +0200
commit530ab3394938331f224058c381a1db5d4a68e6a9 (patch)
tree5060eb394d67b7454855aed0fa8d7d3acf5f5c98 /src/zenserver-test/zenserver-test.cpp
parentfix gc date (#204) (diff)
downloadzen-530ab3394938331f224058c381a1db5d4a68e6a9.tar.xz
zen-530ab3394938331f224058c381a1db5d4a68e6a9.zip
workspace share security (#192)
- Improvement: Reworked workspace shares to be more secure. Workspaces and workspace shares can only be created using the `zen workspace` command, the http endpoint is disabled unless zenserver is started with the `--workspaces-allow-changes` option enabled. - Each workspace are now configured via a `zenworkspaceconfig.json` file in the root of each workspace - A workspace can allow shares to be created via the http interface if the workspace is created with the `--allow-share-create-from-http` option enabled - A new http endpoint at `/ws` - issuing a `Get` operation will get you a list of workspaces - A new http endpoint at `/ws/refresh` - issuing a `Get` will make zenserver scan for edits in workspaces and workspace shares
Diffstat (limited to 'src/zenserver-test/zenserver-test.cpp')
-rw-r--r--src/zenserver-test/zenserver-test.cpp165
1 files changed, 147 insertions, 18 deletions
diff --git a/src/zenserver-test/zenserver-test.cpp b/src/zenserver-test/zenserver-test.cpp
index 15993f5c9..ca2257361 100644
--- a/src/zenserver-test/zenserver-test.cpp
+++ b/src/zenserver-test/zenserver-test.cpp
@@ -3259,16 +3259,22 @@ TEST_CASE("workspaces.create")
std::filesystem::path TestDir = TestEnv.CreateNewTestDir();
ZenServerInstance Instance(TestEnv);
Instance.SetTestDir(TestDir);
- const uint16_t PortNumber = Instance.SpawnServerAndWaitUntilReady(fmt::format("--workspaces-enabled --system-dir {}", SystemRootPath));
+ const uint16_t PortNumber = Instance.SpawnServerAndWaitUntilReady(
+ fmt::format("--workspaces-enabled --workspaces-allow-changes --system-dir {}", SystemRootPath));
CHECK(PortNumber != 0);
ScopedTemporaryDirectory TempDir;
- std::filesystem::path Root1Path = TempDir.Path() / "root1";
- std::filesystem::path Root2Path = TempDir.Path() / "root2";
- std::filesystem::path Share1Path = "shared_1";
- std::filesystem::path Share2Path = "shared_2";
- CreateDirectories(Share1Path);
- CreateDirectories(Share2Path);
+ std::filesystem::path Root1Path = TempDir.Path() / "root1";
+ std::filesystem::path Root2Path = TempDir.Path() / "root2";
+ DeleteDirectories(Root1Path);
+ DeleteDirectories(Root2Path);
+
+ std::filesystem::path Share1Path = "shared_1";
+ std::filesystem::path Share2Path = "shared_2";
+ CreateDirectories(Root1Path / Share1Path);
+ CreateDirectories(Root1Path / Share2Path);
+ CreateDirectories(Root2Path / Share1Path);
+ CreateDirectories(Root2Path / Share2Path);
Oid Root1Id = Oid::Zero;
Oid Root2Id = Oid::NewOid();
@@ -3319,8 +3325,11 @@ TEST_CASE("workspaces.create")
Client.Put(fmt::format("/ws/{}/{}", Root2Id, Oid::Zero), HttpClient::KeyValueMap{{"share_path", Share2Path.string()}}).StatusCode ==
HttpResponseCode::NotFound);
+ CHECK(Client.Put(fmt::format("/ws/{}", Root2Id), HttpClient::KeyValueMap{{"root_path", Root1Path.string()}}).StatusCode ==
+ HttpResponseCode::Conflict);
+
if (HttpClient::Response Root2Response =
- Client.Put(fmt::format("/ws/{}", Root2Id), HttpClient::KeyValueMap{{"root_path", Root1Path.string()}});
+ Client.Put(fmt::format("/ws/{}", Root2Id), HttpClient::KeyValueMap{{"root_path", Root2Path.string()}});
Root2Response.StatusCode == HttpResponseCode::Created)
{
CHECK(Root2Id == Oid::TryFromHexString(Root2Response.AsText()));
@@ -3340,6 +3349,10 @@ TEST_CASE("workspaces.create")
Share2Id = Oid::TryFromHexString(Share2Response.AsText());
CHECK(Share2Id != Oid::Zero);
}
+ else
+ {
+ CHECK(false);
+ }
CHECK(
Client.Put(fmt::format("/ws/{}/{}", Root2Id, Oid::Zero), HttpClient::KeyValueMap{{"share_path", Share2Path.string()}}).StatusCode ==
@@ -3352,6 +3365,117 @@ TEST_CASE("workspaces.create")
CHECK(
Client.Put(fmt::format("/ws/{}/{}", Root2Id, Share2Id), HttpClient::KeyValueMap{{"share_path", Share1Path.string()}}).StatusCode ==
HttpResponseCode::Conflict);
+
+ CHECK(Client.Put(fmt::format("/ws/{}/{}", Root2Id, Oid::NewOid()), HttpClient::KeyValueMap{{"share_path", Share2Path.string()}})
+ .StatusCode == HttpResponseCode::Conflict);
+
+ CHECK(Client.Put(fmt::format("/ws/{}/{}", Root2Id, Oid::Zero), HttpClient::KeyValueMap{{"share_path", "idonotexist"}}).StatusCode !=
+ HttpResponseCode::OK);
+
+ while (true)
+ {
+ std::error_code Ec;
+ std::filesystem::remove_all(Root2Path / Share2Path, Ec);
+ if (!Ec)
+ break;
+ }
+
+ CHECK(Client.Get(fmt::format("/ws/{}/{}/files", Root2Id, Share2Id)).StatusCode == HttpResponseCode::NotFound);
+}
+
+TEST_CASE("workspaces.restricted")
+{
+ using namespace std::literals;
+
+ std::filesystem::path SystemRootPath = TestEnv.CreateNewTestDir();
+
+ std::filesystem::path TestDir = TestEnv.CreateNewTestDir();
+ ZenServerInstance Instance(TestEnv);
+ Instance.SetTestDir(TestDir);
+ const uint16_t PortNumber = Instance.SpawnServerAndWaitUntilReady(fmt::format("--workspaces-enabled --system-dir {}", SystemRootPath));
+ CHECK(PortNumber != 0);
+
+ ScopedTemporaryDirectory TempDir;
+ std::filesystem::path Root1Path = TempDir.Path() / "root1";
+ std::filesystem::path Root2Path = TempDir.Path() / "root2";
+ DeleteDirectories(Root1Path);
+ DeleteDirectories(Root2Path);
+
+ std::filesystem::path Share1Path = "shared_1";
+ std::filesystem::path Share2Path = "shared_2";
+ CreateDirectories(Root1Path / Share1Path);
+ CreateDirectories(Root1Path / Share2Path);
+ CreateDirectories(Root2Path / Share1Path);
+ CreateDirectories(Root2Path / Share2Path);
+
+ Oid Root1Id = Oid::NewOid();
+ Oid Root2Id = Oid::NewOid();
+ Oid Share1Id = Oid::NewOid();
+ Oid Share2Id = Oid::NewOid();
+
+ HttpClient Client(Instance.GetBaseUri());
+ CHECK(Client.Put(fmt::format("/ws/{}", Oid::Zero), HttpClient::KeyValueMap{{"root_path", Root1Path.string()}}).StatusCode ==
+ HttpResponseCode::Unauthorized);
+
+ CHECK_EQ(Client.Get(fmt::format("/ws/{}", Root1Id)).StatusCode, HttpResponseCode::NotFound);
+
+ std::string Config1;
+ {
+ CbObjectWriter Config;
+ Config.BeginArray("workspaces");
+ Config.BeginObject();
+ Config << "id"sv << Root1Id.ToString();
+ Config << "root_path"sv << Root1Path.string();
+ Config << "allow_share_creation_from_http"sv << false;
+ Config.EndObject();
+ Config.EndArray();
+ ExtendableStringBuilder<256> SB;
+ CompactBinaryToJson(Config.Save(), SB);
+ Config1 = SB.ToString();
+ }
+ WriteFile(SystemRootPath / "workspaces" / "config.json", IoBuffer(IoBuffer::Wrap, Config1.data(), Config1.size()));
+
+ CHECK(IsHttpSuccessCode(Client.Get("/ws/refresh").StatusCode));
+
+ CHECK_EQ(Client.Get(fmt::format("/ws/{}", Root1Id)).StatusCode, HttpResponseCode::OK);
+
+ CHECK(Client.Get(fmt::format("/ws/{}/{}", Root1Id, Share1Id)).StatusCode == HttpResponseCode::NotFound);
+ CHECK(
+ Client.Put(fmt::format("/ws/{}/{}", Root1Id, Oid::Zero), HttpClient::KeyValueMap{{"share_path", Share1Path.string()}}).StatusCode ==
+ HttpResponseCode::Unauthorized);
+
+ std::string Config2;
+ {
+ CbObjectWriter Config;
+ Config.BeginArray("workspaces");
+ Config.BeginObject();
+ Config << "id"sv << Root1Id.ToString();
+ Config << "root_path"sv << Root1Path.string();
+ Config << "allow_share_creation_from_http"sv << false;
+ Config.EndObject();
+ Config.BeginObject();
+ Config << "id"sv << Root2Id.ToString();
+ Config << "root_path"sv << Root2Path.string();
+ Config << "allow_share_creation_from_http"sv << true;
+ Config.EndObject();
+ Config.EndArray();
+ ExtendableStringBuilder<256> SB;
+ CompactBinaryToJson(Config.Save(), SB);
+ Config2 = SB.ToString();
+ }
+ WriteFile(SystemRootPath / "workspaces" / "config.json", IoBuffer(IoBuffer::Wrap, Config2.data(), Config2.size()));
+
+ CHECK(IsHttpSuccessCode(Client.Get("/ws/refresh").StatusCode));
+
+ CHECK_EQ(Client.Get(fmt::format("/ws/{}", Root2Id)).StatusCode, HttpResponseCode::OK);
+
+ CHECK(Client.Get(fmt::format("/ws/{}/{}", Root2Id, Share2Id)).StatusCode == HttpResponseCode::NotFound);
+ CHECK(
+ Client.Put(fmt::format("/ws/{}/{}", Root2Id, Share2Id), HttpClient::KeyValueMap{{"share_path", Share2Path.string()}}).StatusCode ==
+ HttpResponseCode::Created);
+ CHECK(Client.Get(fmt::format("/ws/{}/{}", Root2Id, Share2Id)).StatusCode == HttpResponseCode::OK);
+
+ CHECK(IsHttpSuccessCode(Client.Delete(fmt::format("/ws/{}/{}", Root2Id, Share2Id)).StatusCode));
}
TEST_CASE("workspaces.lifetimes")
@@ -3363,19 +3487,20 @@ TEST_CASE("workspaces.lifetimes")
Oid WorkspaceId = Oid::NewOid();
Oid ShareId = Oid::NewOid();
+ ScopedTemporaryDirectory TempDir;
+ std::filesystem::path RootPath = TempDir.Path();
+ DeleteDirectories(RootPath);
+ std::filesystem::path SharePath = RootPath / "shared_folder";
+ CreateDirectories(SharePath);
+
{
std::filesystem::path TestDir = TestEnv.CreateNewTestDir();
ZenServerInstance Instance(TestEnv);
Instance.SetTestDir(TestDir);
- const uint16_t PortNumber =
- Instance.SpawnServerAndWaitUntilReady(fmt::format("--workspaces-enabled --system-dir {}", SystemRootPath));
+ const uint16_t PortNumber = Instance.SpawnServerAndWaitUntilReady(
+ fmt::format("--workspaces-enabled --workspaces-allow-changes --system-dir {}", SystemRootPath));
CHECK(PortNumber != 0);
- ScopedTemporaryDirectory TempDir;
- std::filesystem::path RootPath = TempDir.Path();
- std::filesystem::path SharePath = RootPath / "shared_folder";
- CreateDirectories(SharePath);
-
HttpClient Client(Instance.GetBaseUri());
CHECK(Client.Put(fmt::format("/ws/{}", WorkspaceId), HttpClient::KeyValueMap{{"root_path", RootPath.string()}}).StatusCode ==
HttpResponseCode::Created);
@@ -3427,14 +3552,18 @@ TEST_CASE("workspaces.lifetimes")
TEST_CASE("workspaces.share")
{
+ std::filesystem::path SystemRootPath = TestEnv.CreateNewTestDir();
+
ZenServerInstance Instance(TestEnv);
- const uint16_t PortNumber = Instance.SpawnServerAndWaitUntilReady("--workspaces-enabled");
+ const uint16_t PortNumber = Instance.SpawnServerAndWaitUntilReady(
+ fmt::format("--workspaces-enabled --workspaces-allow-changes --system-dir {}", SystemRootPath));
CHECK(PortNumber != 0);
ScopedTemporaryDirectory TempDir;
- std::filesystem::path RootPath = TempDir.Path();
- std::filesystem::path SharePath = RootPath / "shared_folder";
+ std::filesystem::path RootPath = TempDir.Path();
+ DeleteDirectories(RootPath);
+ std::filesystem::path SharePath = RootPath / "shared_folder";
GenerateFolderContent(SharePath);
HttpClient Client(Instance.GetBaseUri());