From da4bb70c9180c09303caf43dbc746fc641077319 Mon Sep 17 00:00:00 2001 From: khoaguin Date: Tue, 5 Sep 2023 16:37:37 +0700 Subject: [PATCH 01/15] Start tests for MongoDB permissions --- packages/syft/tests/syft/stores/mongo_document_store_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/syft/tests/syft/stores/mongo_document_store_test.py b/packages/syft/tests/syft/stores/mongo_document_store_test.py index 968a94ce0fa..51672f59b02 100644 --- a/packages/syft/tests/syft/stores/mongo_document_store_test.py +++ b/packages/syft/tests/syft/stores/mongo_document_store_test.py @@ -34,6 +34,7 @@ def test_mongo_store_partition_sanity( assert res.is_ok() assert hasattr(mongo_store_partition, "_collection") + assert hasattr(mongo_store_partition, "_permissions") def test_mongo_store_partition_init_failed(root_verify_key) -> None: From 25171828ca8c50f47a7da0a69e9a6e1f324aafe4 Mon Sep 17 00:00:00 2001 From: khoaguin Date: Wed, 6 Sep 2023 11:33:24 +0700 Subject: [PATCH 02/15] on writing test for of MongoStorePartition --- .../src/syft/store/mongo_document_store.py | 6 +- .../syft/stores/mongo_document_store_test.py | 76 +++++++++++++++++++ 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/packages/syft/src/syft/store/mongo_document_store.py b/packages/syft/src/syft/store/mongo_document_store.py index 46664512647..0f83db26b1d 100644 --- a/packages/syft/src/syft/store/mongo_document_store.py +++ b/packages/syft/src/syft/store/mongo_document_store.py @@ -3,6 +3,7 @@ from typing import Dict from typing import List from typing import Optional +from typing import Set from typing import Type # third party @@ -450,7 +451,7 @@ def add_permission(self, permission: ActionObjectPermission) -> Result[None, Err ) else: # update the permissions with the new permission string - permission_strings: set = permissions["permissions"] + permission_strings: Set = permissions["permissions"] permission_strings.add(permission.permission_string) collection_permissions.update_one( {"_id": permission.uid}, {"$set": {"permissions": permission_strings}} @@ -472,7 +473,7 @@ def remove_permission( ) if permissions is None: return Err(f"permission with UID {permission.uid} not found!") - permissions_strings: set = permissions["permissions"] + permissions_strings: Set = permissions["permissions"] permissions_strings.remove(permission.permission_string) collection_permissions.update_one( {"_id": permission.uid}, {"$set": {"permissions": permissions_strings}} @@ -481,6 +482,7 @@ def remove_permission( def take_ownership( self, uid: UID, credentials: SyftVerifyKey ) -> Result[SyftSuccess, str]: + print("taking ownership!!!") collection_permissions_status = self.permissions if collection_permissions_status.is_err(): return collection_permissions_status diff --git a/packages/syft/tests/syft/stores/mongo_document_store_test.py b/packages/syft/tests/syft/stores/mongo_document_store_test.py index 51672f59b02..7bebd9a391a 100644 --- a/packages/syft/tests/syft/stores/mongo_document_store_test.py +++ b/packages/syft/tests/syft/stores/mongo_document_store_test.py @@ -1,14 +1,19 @@ # stdlib import sys from threading import Thread +from typing import Set from typing import Tuple # third party from joblib import Parallel from joblib import delayed +from pymongo.collection import Collection as MongoCollection import pytest # syft absolute +from syft.node.credentials import SyftVerifyKey +from syft.service.action.action_permissions import ActionObjectPermission +from syft.service.action.action_permissions import ActionPermission from syft.store.document_store import PartitionSettings from syft.store.document_store import QueryKeys from syft.store.mongo_client import MongoStoreClientConfig @@ -600,3 +605,74 @@ def _kv_cbk(tid: int) -> None: ).ok() ) assert stored_cnt == 0 + + +def test_mongo_store_partition_permissions_collection( + mongo_store_partition: MongoStorePartition, +) -> None: + res = mongo_store_partition.init_store() + assert res.is_ok() + + collection_permissions_status = mongo_store_partition.permissions + assert not collection_permissions_status.is_err() + collection_permissions = collection_permissions_status.ok() + assert isinstance(collection_permissions, MongoCollection) + + +def test_mongo_store_partition_add_permission( + root_verify_key: SyftVerifyKey, mongo_store_partition: MongoStorePartition +) -> None: + res = mongo_store_partition.init_store() + assert res.is_ok() + + permissions_collection: MongoCollection = mongo_store_partition.permissions.ok() + obj = MockSyftObject(data=1) + obj_read_permission = ActionObjectPermission( + uid=obj.id, permission=ActionPermission.READ, credentials=root_verify_key + ) + print(permissions_collection, obj_read_permission) + + +def test_mongo_store_partition_add_permissions( + mongo_store_partition: MongoStorePartition, +) -> None: + pass + + +def test_mongo_store_partition_take_ownership( + mongo_store_partition: MongoStorePartition, +) -> None: + pass + + +def test_mongo_store_partition_remove_permission( + mongo_store_partition: MongoStorePartition, +) -> None: + pass + + +def test_mongo_store_partition_permissions_set( + root_verify_key: SyftVerifyKey, mongo_store_partition: MongoStorePartition +) -> None: + """ + Test the permissions functionalities when using MongoStorePartition._set function + """ + res = mongo_store_partition.init_store() + assert res.is_ok() + + # set the object to mongo_store_partition.collection + obj = MockSyftObject(data=1) + res = mongo_store_partition.set(root_verify_key, obj, ignore_duplicates=False) + assert res.is_ok() + assert res.ok() == obj + + # check if the corresponding permission has been added to the + # mongo_store_partition.permissions collection + pemissions_collection = mongo_store_partition.permissions.ok() + assert isinstance(pemissions_collection, MongoCollection) + permissions = pemissions_collection.find_one({"_id": obj.id}) + assert permissions is not None + assert isinstance(permissions["permissions"], Set) + assert len(permissions["permissions"]) == 4 + + # import pdb; pdb.set_trace() From 45af0a6f6d9828e96841ea6636860456ecdd8aa0 Mon Sep 17 00:00:00 2001 From: khoaguin Date: Wed, 6 Sep 2023 16:29:06 +0700 Subject: [PATCH 03/15] Test the add_permission and remove_permission functions of MongoStorePartition --- .../src/syft/store/mongo_document_store.py | 13 ++-- .../syft/stores/mongo_document_store_test.py | 74 +++++++++++++++++-- 2 files changed, 77 insertions(+), 10 deletions(-) diff --git a/packages/syft/src/syft/store/mongo_document_store.py b/packages/syft/src/syft/store/mongo_document_store.py index 0f83db26b1d..acc31011202 100644 --- a/packages/syft/src/syft/store/mongo_document_store.py +++ b/packages/syft/src/syft/store/mongo_document_store.py @@ -442,7 +442,7 @@ def add_permission(self, permission: ActionObjectPermission) -> Result[None, Err {"_id": permission.uid} ) if permissions is None: - # Permission doesn't exits, add a new one + # Permission doesn't exist, add a new one collection_permissions.insert_one( { "_id": permission.uid, @@ -474,10 +474,13 @@ def remove_permission( if permissions is None: return Err(f"permission with UID {permission.uid} not found!") permissions_strings: Set = permissions["permissions"] - permissions_strings.remove(permission.permission_string) - collection_permissions.update_one( - {"_id": permission.uid}, {"$set": {"permissions": permissions_strings}} - ) + if permission.permission_string in permissions_strings: + permissions_strings.remove(permission.permission_string) + collection_permissions.update_one( + {"_id": permission.uid}, {"$set": {"permissions": permissions_strings}} + ) + else: + return Err(f"the permission {permission.permission_string} does not exist!") def take_ownership( self, uid: UID, credentials: SyftVerifyKey diff --git a/packages/syft/tests/syft/stores/mongo_document_store_test.py b/packages/syft/tests/syft/stores/mongo_document_store_test.py index 7bebd9a391a..33da69b71c2 100644 --- a/packages/syft/tests/syft/stores/mongo_document_store_test.py +++ b/packages/syft/tests/syft/stores/mongo_document_store_test.py @@ -9,6 +9,7 @@ from joblib import delayed from pymongo.collection import Collection as MongoCollection import pytest +from result import Err # syft absolute from syft.node.credentials import SyftVerifyKey @@ -619,38 +620,103 @@ def test_mongo_store_partition_permissions_collection( assert isinstance(collection_permissions, MongoCollection) -def test_mongo_store_partition_add_permission( +def test_mongo_store_partition_add_remove_permission( root_verify_key: SyftVerifyKey, mongo_store_partition: MongoStorePartition ) -> None: + """ + Test the add_permission and remove_permission functions of MongoStorePartition + """ + # setting up res = mongo_store_partition.init_store() assert res.is_ok() - permissions_collection: MongoCollection = mongo_store_partition.permissions.ok() obj = MockSyftObject(data=1) + + # add the first permission obj_read_permission = ActionObjectPermission( uid=obj.id, permission=ActionPermission.READ, credentials=root_verify_key ) - print(permissions_collection, obj_read_permission) + mongo_store_partition.add_permission(obj_read_permission) + assert permissions_collection.count_documents({}) == 1 + find_res_1 = permissions_collection.find_one({"_id": obj_read_permission.uid}) + assert find_res_1 is not None + assert len(find_res_1["permissions"]) == 1 + assert find_res_1["permissions"] == { + obj_read_permission.permission_string, + } + + # add the second permission + obj_write_permission = ActionObjectPermission( + uid=obj.id, permission=ActionPermission.WRITE, credentials=root_verify_key + ) + mongo_store_partition.add_permission(obj_write_permission) + assert permissions_collection.count_documents({}) == 1 + + find_res_2 = permissions_collection.find_one({"_id": obj_read_permission.uid}) + assert find_res_2 is not None + assert len(find_res_2["permissions"]) == 2 + assert find_res_2["permissions"] == { + obj_read_permission.permission_string, + obj_write_permission.permission_string, + } + + # add duplicated permission + mongo_store_partition.add_permission(obj_write_permission) + assert permissions_collection.count_documents({}) == 1 + find_res_3 = permissions_collection.find_one({"_id": obj_read_permission.uid}) + assert len(find_res_3["permissions"]) == 2 + assert find_res_3["permissions"] == find_res_2["permissions"] + + # remove the write permission + mongo_store_partition.remove_permission(obj_write_permission) + find_res_4 = permissions_collection.find_one({"_id": obj_read_permission.uid}) + assert len(find_res_4["permissions"]) == 1 + assert find_res_1["permissions"] == { + obj_read_permission.permission_string, + } + + # remove a non-existent permission + remove_res = mongo_store_partition.remove_permission( + ActionObjectPermission( + uid=obj.id, permission=ActionPermission.OWNER, credentials=root_verify_key + ) + ) + assert isinstance(remove_res, Err) + find_res_5 = permissions_collection.find_one({"_id": obj_read_permission.uid}) + assert len(find_res_5["permissions"]) == 1 + assert find_res_1["permissions"] == { + obj_read_permission.permission_string, + } +@pytest.mark.skip(reason="To be implemented") def test_mongo_store_partition_add_permissions( mongo_store_partition: MongoStorePartition, ) -> None: pass +@pytest.mark.skip(reason="To be implemented") def test_mongo_store_partition_take_ownership( mongo_store_partition: MongoStorePartition, ) -> None: pass +@pytest.mark.skip(reason="To be implemented") def test_mongo_store_partition_remove_permission( mongo_store_partition: MongoStorePartition, ) -> None: pass +@pytest.mark.skip(reason="To be implemented") +def test_mongo_store_partition_has_permission( + mongo_store_partition: MongoStorePartition, +) -> None: + pass + + def test_mongo_store_partition_permissions_set( root_verify_key: SyftVerifyKey, mongo_store_partition: MongoStorePartition ) -> None: @@ -674,5 +740,3 @@ def test_mongo_store_partition_permissions_set( assert permissions is not None assert isinstance(permissions["permissions"], Set) assert len(permissions["permissions"]) == 4 - - # import pdb; pdb.set_trace() From edbe147a1538871b23d7512e7b98f786eeeeb4c2 Mon Sep 17 00:00:00 2001 From: khoaguin Date: Thu, 7 Sep 2023 19:25:11 +0700 Subject: [PATCH 04/15] delete the permissions when there is no permission strings left. more tests on add_permission, remove_permission, add_permissions --- .../src/syft/store/mongo_document_store.py | 11 ++- packages/syft/tests/conftest.py | 5 + .../syft/stores/mongo_document_store_test.py | 91 +++++++++++++++---- 3 files changed, 85 insertions(+), 22 deletions(-) diff --git a/packages/syft/src/syft/store/mongo_document_store.py b/packages/syft/src/syft/store/mongo_document_store.py index acc31011202..8feb89b924d 100644 --- a/packages/syft/src/syft/store/mongo_document_store.py +++ b/packages/syft/src/syft/store/mongo_document_store.py @@ -476,16 +476,19 @@ def remove_permission( permissions_strings: Set = permissions["permissions"] if permission.permission_string in permissions_strings: permissions_strings.remove(permission.permission_string) - collection_permissions.update_one( - {"_id": permission.uid}, {"$set": {"permissions": permissions_strings}} - ) + if len(permissions_strings) > 0: + collection_permissions.update_one( + {"_id": permission.uid}, + {"$set": {"permissions": permissions_strings}}, + ) + else: + collection_permissions.delete_one({"_id": permission.uid}) else: return Err(f"the permission {permission.permission_string} does not exist!") def take_ownership( self, uid: UID, credentials: SyftVerifyKey ) -> Result[SyftSuccess, str]: - print("taking ownership!!!") collection_permissions_status = self.permissions if collection_permissions_status.is_err(): return collection_permissions_status diff --git a/packages/syft/tests/conftest.py b/packages/syft/tests/conftest.py index 53bc1c2260b..734faf9d5a5 100644 --- a/packages/syft/tests/conftest.py +++ b/packages/syft/tests/conftest.py @@ -46,6 +46,11 @@ def guest_client(worker): return worker.guest_client +@pytest.fixture(autouse=True) +def guest_verify_key(worker): + return worker.guest_client.credentials.verify_key + + @pytest.fixture(autouse=True) def guest_domain_client(root_domain_client): return root_domain_client.guest() diff --git a/packages/syft/tests/syft/stores/mongo_document_store_test.py b/packages/syft/tests/syft/stores/mongo_document_store_test.py index 33da69b71c2..2dfbbac9053 100644 --- a/packages/syft/tests/syft/stores/mongo_document_store_test.py +++ b/packages/syft/tests/syft/stores/mongo_document_store_test.py @@ -1,6 +1,7 @@ # stdlib import sys from threading import Thread +from typing import List from typing import Set from typing import Tuple @@ -238,7 +239,7 @@ def test_mongo_store_partition_update( res = mongo_store_partition.update(root_verify_key, key, obj_new) assert res.is_ok() - # The ID should stay the same on update, unly the values are updated. + # The ID should stay the same on update, only the values are updated. assert ( len( mongo_store_partition.all( @@ -637,7 +638,6 @@ def test_mongo_store_partition_add_remove_permission( uid=obj.id, permission=ActionPermission.READ, credentials=root_verify_key ) mongo_store_partition.add_permission(obj_read_permission) - assert permissions_collection.count_documents({}) == 1 find_res_1 = permissions_collection.find_one({"_id": obj_read_permission.uid}) assert find_res_1 is not None assert len(find_res_1["permissions"]) == 1 @@ -650,9 +650,8 @@ def test_mongo_store_partition_add_remove_permission( uid=obj.id, permission=ActionPermission.WRITE, credentials=root_verify_key ) mongo_store_partition.add_permission(obj_write_permission) - assert permissions_collection.count_documents({}) == 1 - find_res_2 = permissions_collection.find_one({"_id": obj_read_permission.uid}) + find_res_2 = permissions_collection.find_one({"_id": obj.id}) assert find_res_2 is not None assert len(find_res_2["permissions"]) == 2 assert find_res_2["permissions"] == { @@ -662,14 +661,13 @@ def test_mongo_store_partition_add_remove_permission( # add duplicated permission mongo_store_partition.add_permission(obj_write_permission) - assert permissions_collection.count_documents({}) == 1 - find_res_3 = permissions_collection.find_one({"_id": obj_read_permission.uid}) + find_res_3 = permissions_collection.find_one({"_id": obj.id}) assert len(find_res_3["permissions"]) == 2 assert find_res_3["permissions"] == find_res_2["permissions"] # remove the write permission mongo_store_partition.remove_permission(obj_write_permission) - find_res_4 = permissions_collection.find_one({"_id": obj_read_permission.uid}) + find_res_4 = permissions_collection.find_one({"_id": obj.id}) assert len(find_res_4["permissions"]) == 1 assert find_res_1["permissions"] == { obj_read_permission.permission_string, @@ -682,29 +680,86 @@ def test_mongo_store_partition_add_remove_permission( ) ) assert isinstance(remove_res, Err) - find_res_5 = permissions_collection.find_one({"_id": obj_read_permission.uid}) + find_res_5 = permissions_collection.find_one({"_id": obj.id}) assert len(find_res_5["permissions"]) == 1 assert find_res_1["permissions"] == { obj_read_permission.permission_string, } + # there is only one permission object + assert permissions_collection.count_documents({}) == 1 + + # add permissions in a loop + new_permissions = [] + for idx in range(1, REPEATS + 1): + new_obj = MockSyftObject(data=idx) + new_obj_read_permission = ActionObjectPermission( + uid=new_obj.id, + permission=ActionPermission.READ, + credentials=root_verify_key, + ) + new_permissions.append(new_obj_read_permission) + mongo_store_partition.add_permission(new_obj_read_permission) + assert permissions_collection.count_documents({}) == 1 + idx + + # remove the permissions + for permission in new_permissions: + mongo_store_partition.remove_permission(permission) + + assert permissions_collection.count_documents({}) == 1 + -@pytest.mark.skip(reason="To be implemented") def test_mongo_store_partition_add_permissions( + root_verify_key: SyftVerifyKey, + guest_verify_key: SyftVerifyKey, mongo_store_partition: MongoStorePartition, ) -> None: - pass + res = mongo_store_partition.init_store() + assert res.is_ok() + permissions_collection: MongoCollection = mongo_store_partition.permissions.ok() + obj = MockSyftObject(data=1) + # add multiple permissions for the first object + permission_1 = ActionObjectPermission( + uid=obj.id, permission=ActionPermission.WRITE, credentials=root_verify_key + ) + permission_2 = ActionObjectPermission( + uid=obj.id, permission=ActionPermission.OWNER, credentials=root_verify_key + ) + permission_3 = ActionObjectPermission( + uid=obj.id, permission=ActionPermission.READ, credentials=guest_verify_key + ) + permissions: List[ActionObjectPermission] = [ + permission_1, + permission_2, + permission_3, + ] + mongo_store_partition.add_permissions(permissions) + + # check if the permissions have been added properly + assert permissions_collection.count_documents({}) == 1 + find_res = permissions_collection.find_one({"_id": obj.id}) + assert find_res is not None + assert len(find_res["permissions"]) == 3 + + # add permissions for the second object + obj_2 = MockSyftObject(data=2) + permission_4 = ActionObjectPermission( + uid=obj_2.id, permission=ActionPermission.READ, credentials=root_verify_key + ) + permission_5 = ActionObjectPermission( + uid=obj_2.id, permission=ActionPermission.WRITE, credentials=root_verify_key + ) + mongo_store_partition.add_permissions([permission_4, permission_5]) -@pytest.mark.skip(reason="To be implemented") -def test_mongo_store_partition_take_ownership( - mongo_store_partition: MongoStorePartition, -) -> None: - pass + assert permissions_collection.count_documents({}) == 2 + find_res_2 = permissions_collection.find_one({"_id": obj_2.id}) + assert find_res_2 is not None + assert len(find_res_2["permissions"]) == 2 @pytest.mark.skip(reason="To be implemented") -def test_mongo_store_partition_remove_permission( +def test_mongo_store_partition_take_ownership( mongo_store_partition: MongoStorePartition, ) -> None: pass @@ -732,8 +787,8 @@ def test_mongo_store_partition_permissions_set( assert res.is_ok() assert res.ok() == obj - # check if the corresponding permission has been added to the - # mongo_store_partition.permissions collection + # check if the corresponding permission has been added + # to the mongo_store_partition.permissions collection pemissions_collection = mongo_store_partition.permissions.ok() assert isinstance(pemissions_collection, MongoCollection) permissions = pemissions_collection.find_one({"_id": obj.id}) From f37f387165c0c860a4a74a7ac9891897b9091b00 Mon Sep 17 00:00:00 2001 From: khoaguin Date: Mon, 11 Sep 2023 13:14:23 +0700 Subject: [PATCH 05/15] skip tests if on win32. on testing `has_permission` and `take_ownership` --- .../syft/stores/mongo_document_store_test.py | 50 ++++++++++++++++--- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/packages/syft/tests/syft/stores/mongo_document_store_test.py b/packages/syft/tests/syft/stores/mongo_document_store_test.py index 2dfbbac9053..830aa205125 100644 --- a/packages/syft/tests/syft/stores/mongo_document_store_test.py +++ b/packages/syft/tests/syft/stores/mongo_document_store_test.py @@ -16,6 +16,7 @@ from syft.node.credentials import SyftVerifyKey from syft.service.action.action_permissions import ActionObjectPermission from syft.service.action.action_permissions import ActionPermission +from syft.service.action.action_store import ActionObjectREAD from syft.store.document_store import PartitionSettings from syft.store.document_store import QueryKeys from syft.store.mongo_client import MongoStoreClientConfig @@ -24,6 +25,9 @@ # relative from .store_constants_test import generate_db_name +from .store_constants_test import test_verify_key_string_client +from .store_constants_test import test_verify_key_string_hacker +from .store_constants_test import test_verify_key_string_root from .store_fixtures_test import mongo_store_partition_fn from .store_mocks_test import MockObjectType from .store_mocks_test import MockSyftObject @@ -609,6 +613,9 @@ def _kv_cbk(tid: int) -> None: assert stored_cnt == 0 +@pytest.mark.skipif( + sys.platform == "win32", reason="pytest_mock_resources + docker issues on Windows" +) def test_mongo_store_partition_permissions_collection( mongo_store_partition: MongoStorePartition, ) -> None: @@ -621,6 +628,9 @@ def test_mongo_store_partition_permissions_collection( assert isinstance(collection_permissions, MongoCollection) +@pytest.mark.skipif( + sys.platform == "win32", reason="pytest_mock_resources + docker issues on Windows" +) def test_mongo_store_partition_add_remove_permission( root_verify_key: SyftVerifyKey, mongo_store_partition: MongoStorePartition ) -> None: @@ -709,6 +719,9 @@ def test_mongo_store_partition_add_remove_permission( assert permissions_collection.count_documents({}) == 1 +@pytest.mark.skipif( + sys.platform == "win32", reason="pytest_mock_resources + docker issues on Windows" +) def test_mongo_store_partition_add_permissions( root_verify_key: SyftVerifyKey, guest_verify_key: SyftVerifyKey, @@ -758,20 +771,45 @@ def test_mongo_store_partition_add_permissions( assert len(find_res_2["permissions"]) == 2 -@pytest.mark.skip(reason="To be implemented") -def test_mongo_store_partition_take_ownership( +@pytest.mark.skipif( + sys.platform == "win32", reason="pytest_mock_resources + docker issues on Windows" +) +def test_mongo_store_partition_has_permission( mongo_store_partition: MongoStorePartition, ) -> None: - pass + SyftVerifyKey.from_string(test_verify_key_string_client) + root_key = SyftVerifyKey.from_string(test_verify_key_string_root) + SyftVerifyKey.from_string(test_verify_key_string_hacker) + res = mongo_store_partition.init_store() + assert res.is_ok() + mongo_store_partition.permissions.ok() + obj = MockSyftObject(data=1) -@pytest.mark.skip(reason="To be implemented") -def test_mongo_store_partition_has_permission( + permission_1 = ActionObjectPermission( + uid=obj.id, permission=ActionPermission.READ, credentials=root_key + ) + mongo_store_partition.add_permission(permission_1) + + assert mongo_store_partition.has_permission( + ActionObjectREAD(uid=obj.id, credentials=root_key) + ) + + +@pytest.mark.skipif( + sys.platform == "win32", reason="pytest_mock_resources + docker issues on Windows" +) +def test_mongo_store_partition_take_ownership( mongo_store_partition: MongoStorePartition, ) -> None: - pass + SyftVerifyKey.from_string(test_verify_key_string_client) + SyftVerifyKey.from_string(test_verify_key_string_root) + SyftVerifyKey.from_string(test_verify_key_string_hacker) +@pytest.mark.skipif( + sys.platform == "win32", reason="pytest_mock_resources + docker issues on Windows" +) def test_mongo_store_partition_permissions_set( root_verify_key: SyftVerifyKey, mongo_store_partition: MongoStorePartition ) -> None: From f38179227247aa97d83e8dd08c2d116a3363923f Mon Sep 17 00:00:00 2001 From: khoaguin Date: Mon, 11 Sep 2023 16:00:58 +0700 Subject: [PATCH 06/15] - add tests for `has_permission` - return `True` for `has_permission` after checking if the permission exists --- .../src/syft/store/mongo_document_store.py | 8 +-- .../syft/stores/mongo_document_store_test.py | 68 ++++++++++++++----- 2 files changed, 54 insertions(+), 22 deletions(-) diff --git a/packages/syft/src/syft/store/mongo_document_store.py b/packages/syft/src/syft/store/mongo_document_store.py index 8feb89b924d..78e8e99f509 100644 --- a/packages/syft/src/syft/store/mongo_document_store.py +++ b/packages/syft/src/syft/store/mongo_document_store.py @@ -403,10 +403,6 @@ def has_permission(self, permission: ActionObjectPermission) -> bool: return False collection_permissions: MongoCollection = collection_permissions_status.ok() - # TODO: fix for other admins - if self.root_verify_key.verify == permission.credentials.verify: - return True - permissions: Optional[Dict] = collection_permissions.find_one( {"_id": permission.uid} ) @@ -414,6 +410,10 @@ def has_permission(self, permission: ActionObjectPermission) -> bool: if permissions is None: return False + # TODO: fix for other admins + if self.root_verify_key.verify == permission.credentials.verify: + return True + if permission.permission_string in permissions["permissions"]: return True diff --git a/packages/syft/tests/syft/stores/mongo_document_store_test.py b/packages/syft/tests/syft/stores/mongo_document_store_test.py index 830aa205125..ac317bdcd1e 100644 --- a/packages/syft/tests/syft/stores/mongo_document_store_test.py +++ b/packages/syft/tests/syft/stores/mongo_document_store_test.py @@ -16,7 +16,10 @@ from syft.node.credentials import SyftVerifyKey from syft.service.action.action_permissions import ActionObjectPermission from syft.service.action.action_permissions import ActionPermission +from syft.service.action.action_store import ActionObjectEXECUTE +from syft.service.action.action_store import ActionObjectOWNER from syft.service.action.action_store import ActionObjectREAD +from syft.service.action.action_store import ActionObjectWRITE from syft.store.document_store import PartitionSettings from syft.store.document_store import QueryKeys from syft.store.mongo_client import MongoStoreClientConfig @@ -25,15 +28,20 @@ # relative from .store_constants_test import generate_db_name -from .store_constants_test import test_verify_key_string_client from .store_constants_test import test_verify_key_string_hacker -from .store_constants_test import test_verify_key_string_root from .store_fixtures_test import mongo_store_partition_fn from .store_mocks_test import MockObjectType from .store_mocks_test import MockSyftObject REPEATS = 20 +PERMISSIONS = [ + ActionObjectOWNER, + ActionObjectREAD, + ActionObjectWRITE, + ActionObjectEXECUTE, +] + @pytest.mark.skipif( sys.platform != "linux", reason="pytest_mock_resources + docker issues on Windows" @@ -712,7 +720,7 @@ def test_mongo_store_partition_add_remove_permission( mongo_store_partition.add_permission(new_obj_read_permission) assert permissions_collection.count_documents({}) == 1 + idx - # remove the permissions + # remove all the permissions added in the loop for permission in new_permissions: mongo_store_partition.remove_permission(permission) @@ -774,36 +782,60 @@ def test_mongo_store_partition_add_permissions( @pytest.mark.skipif( sys.platform == "win32", reason="pytest_mock_resources + docker issues on Windows" ) +@pytest.mark.parametrize("permission", PERMISSIONS) def test_mongo_store_partition_has_permission( + root_verify_key: SyftVerifyKey, + guest_verify_key: SyftVerifyKey, mongo_store_partition: MongoStorePartition, + permission: ActionObjectPermission, ) -> None: - SyftVerifyKey.from_string(test_verify_key_string_client) - root_key = SyftVerifyKey.from_string(test_verify_key_string_root) - SyftVerifyKey.from_string(test_verify_key_string_hacker) + hacker_key = SyftVerifyKey.from_string(test_verify_key_string_hacker) res = mongo_store_partition.init_store() assert res.is_ok() - mongo_store_partition.permissions.ok() - obj = MockSyftObject(data=1) - permission_1 = ActionObjectPermission( - uid=obj.id, permission=ActionPermission.READ, credentials=root_key - ) - mongo_store_partition.add_permission(permission_1) - - assert mongo_store_partition.has_permission( - ActionObjectREAD(uid=obj.id, credentials=root_key) - ) + # root permission + obj = MockSyftObject(data=1) + permission_root = permission(uid=obj.id, credentials=root_verify_key) + permission_client = permission(uid=obj.id, credentials=guest_verify_key) + permission_hacker = permission(uid=obj.id, credentials=hacker_key) + mongo_store_partition.add_permission(permission_root) + # only the root user has access to this permission + assert mongo_store_partition.has_permission(permission_root) + assert not mongo_store_partition.has_permission(permission_client) + assert not mongo_store_partition.has_permission(permission_hacker) + + # client permission for another object + obj_2 = MockSyftObject(data=2) + permission_client_2 = permission(uid=obj_2.id, credentials=guest_verify_key) + permission_root_2 = permission(uid=obj_2.id, credentials=root_verify_key) + permisson_hacker_2 = permission(uid=obj_2.id, credentials=hacker_key) + mongo_store_partition.add_permission(permission_client_2) + # the root (admin) and guest client should have this permission + assert mongo_store_partition.has_permission(permission_root_2) + assert mongo_store_partition.has_permission(permission_client_2) + assert not mongo_store_partition.has_permission(permisson_hacker_2) + + # remove permissions + mongo_store_partition.remove_permission(permission_root) + assert not mongo_store_partition.has_permission(permission_root) + assert not mongo_store_partition.has_permission(permission_client) + assert not mongo_store_partition.has_permission(permission_hacker) + + mongo_store_partition.remove_permission(permission_client_2) + assert not mongo_store_partition.has_permission(permission_root_2) + assert not mongo_store_partition.has_permission(permission_client_2) + assert not mongo_store_partition.has_permission(permisson_hacker_2) @pytest.mark.skipif( sys.platform == "win32", reason="pytest_mock_resources + docker issues on Windows" ) def test_mongo_store_partition_take_ownership( + root_verify_key: SyftVerifyKey, + guest_verify_key: SyftVerifyKey, mongo_store_partition: MongoStorePartition, ) -> None: - SyftVerifyKey.from_string(test_verify_key_string_client) - SyftVerifyKey.from_string(test_verify_key_string_root) SyftVerifyKey.from_string(test_verify_key_string_hacker) From 9eec15c782550276de518b982c38dc537d870471 Mon Sep 17 00:00:00 2001 From: khoaguin Date: Tue, 12 Sep 2023 09:13:19 +0700 Subject: [PATCH 07/15] test for `take_ownership` --- .../syft/stores/mongo_document_store_test.py | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/packages/syft/tests/syft/stores/mongo_document_store_test.py b/packages/syft/tests/syft/stores/mongo_document_store_test.py index ac317bdcd1e..0214d6b681a 100644 --- a/packages/syft/tests/syft/stores/mongo_document_store_test.py +++ b/packages/syft/tests/syft/stores/mongo_document_store_test.py @@ -831,12 +831,51 @@ def test_mongo_store_partition_has_permission( @pytest.mark.skipif( sys.platform == "win32", reason="pytest_mock_resources + docker issues on Windows" ) +@pytest.mark.parametrize("permission", PERMISSIONS) def test_mongo_store_partition_take_ownership( root_verify_key: SyftVerifyKey, guest_verify_key: SyftVerifyKey, mongo_store_partition: MongoStorePartition, + permission: ActionObjectPermission, ) -> None: - SyftVerifyKey.from_string(test_verify_key_string_hacker) + hacker_key = SyftVerifyKey.from_string(test_verify_key_string_hacker) + obj = MockSyftObject(data=1) + + # the guest client takes ownership of obj + mongo_store_partition.take_ownership(uid=obj.id, credentials=guest_verify_key) + assert mongo_store_partition.has_permission( + permission(uid=obj.id, credentials=guest_verify_key) + ) + # the root client will also has the permission + assert mongo_store_partition.has_permission( + permission(uid=obj.id, credentials=root_verify_key) + ) + assert not mongo_store_partition.has_permission( + permission(uid=obj.id, credentials=hacker_key) + ) + + # hacker or root try to take ownership of the obj and will fail + res = mongo_store_partition.take_ownership(uid=obj.id, credentials=hacker_key) + res_2 = mongo_store_partition.take_ownership( + uid=obj.id, credentials=root_verify_key + ) + assert res.is_err() + assert res_2.is_err() + assert res.value == res_2.value == f"UID: {obj.id} already owned." + + # another object + obj_2 = MockSyftObject(data=2) + # root client takes ownership + mongo_store_partition.take_ownership(uid=obj_2.id, credentials=root_verify_key) + assert mongo_store_partition.has_permission( + permission(uid=obj_2.id, credentials=root_verify_key) + ) + assert not mongo_store_partition.has_permission( + permission(uid=obj_2.id, credentials=guest_verify_key) + ) + assert not mongo_store_partition.has_permission( + permission(uid=obj_2.id, credentials=hacker_key) + ) @pytest.mark.skipif( From 2777d1143ee98335f8249d756eacd2fd64683815 Mon Sep 17 00:00:00 2001 From: khoaguin Date: Tue, 12 Sep 2023 13:15:58 +0700 Subject: [PATCH 08/15] Increase `serverSelectionTimeoutMS` for `MongoStoreClientConfig` to fix some failed tests in `mongo_document_store_test.py` Co-authored-by: Shubham Gupta --- packages/syft/src/syft/store/mongo_client.py | 2 +- .../syft/stores/mongo_document_store_test.py | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/syft/src/syft/store/mongo_client.py b/packages/syft/src/syft/store/mongo_client.py index f5c8ea47b05..7dd185ca722 100644 --- a/packages/syft/src/syft/store/mongo_client.py +++ b/packages/syft/src/syft/store/mongo_client.py @@ -108,7 +108,7 @@ class MongoStoreClientConfig(StoreClientConfig): timeoutMS: int = 0 socketTimeoutMS: int = 0 connectTimeoutMS: int = 20000 - serverSelectionTimeoutMS: int = 30000 + serverSelectionTimeoutMS: int = 120000 waitQueueTimeoutMS: Optional[int] = None heartbeatFrequencyMS: int = 10000 appname: str = "pysyft" diff --git a/packages/syft/tests/syft/stores/mongo_document_store_test.py b/packages/syft/tests/syft/stores/mongo_document_store_test.py index 0214d6b681a..3b79a7ca1f9 100644 --- a/packages/syft/tests/syft/stores/mongo_document_store_test.py +++ b/packages/syft/tests/syft/stores/mongo_document_store_test.py @@ -896,7 +896,7 @@ def test_mongo_store_partition_permissions_set( assert res.is_ok() assert res.ok() == obj - # check if the corresponding permission has been added + # check if the corresponding permissions has been added # to the mongo_store_partition.permissions collection pemissions_collection = mongo_store_partition.permissions.ok() assert isinstance(pemissions_collection, MongoCollection) @@ -904,3 +904,18 @@ def test_mongo_store_partition_permissions_set( assert permissions is not None assert isinstance(permissions["permissions"], Set) assert len(permissions["permissions"]) == 4 + + +@pytest.mark.skip(reason="To be implemented") +def test_mongo_store_partition_permissions_update() -> None: + return None + + +@pytest.mark.skip(reason="To be implemented") +def test_mongo_store_partition_permissions_delete() -> None: + return None + + +@pytest.mark.skip(reason="To be implemented") +def test_mongo_store_partition_permissions_get_all() -> None: + return None From e217cc3dcb1628f50bd2670462423abad0a3e0f0 Mon Sep 17 00:00:00 2001 From: khoaguin Date: Wed, 13 Sep 2023 15:21:39 +0700 Subject: [PATCH 09/15] testing permissions when using the `set` method of MongoStorePartition --- packages/syft/src/syft/store/mongo_client.py | 2 +- .../syft/stores/mongo_document_store_test.py | 37 +++++++++++++++---- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/packages/syft/src/syft/store/mongo_client.py b/packages/syft/src/syft/store/mongo_client.py index 7dd185ca722..c20b056777f 100644 --- a/packages/syft/src/syft/store/mongo_client.py +++ b/packages/syft/src/syft/store/mongo_client.py @@ -73,7 +73,7 @@ class MongoStoreClientConfig(StoreClientConfig): Controls how long (in milliseconds) the driver will wait to find an available, appropriate server to carry out a database operation; while it is waiting, multiple server monitoring operations may be carried out, each controlled by `connectTimeoutMS`. - Defaults to ``30000`` (30 seconds). + Defaults to ``120000`` (120 seconds). `waitQueueTimeoutMS`: (integer or None) How long (in milliseconds) a thread will wait for a socket from the pool if the pool has no free sockets. Defaults to ``None`` (no timeout). diff --git a/packages/syft/tests/syft/stores/mongo_document_store_test.py b/packages/syft/tests/syft/stores/mongo_document_store_test.py index 3b79a7ca1f9..de8caa20fac 100644 --- a/packages/syft/tests/syft/stores/mongo_document_store_test.py +++ b/packages/syft/tests/syft/stores/mongo_document_store_test.py @@ -789,7 +789,7 @@ def test_mongo_store_partition_has_permission( mongo_store_partition: MongoStorePartition, permission: ActionObjectPermission, ) -> None: - hacker_key = SyftVerifyKey.from_string(test_verify_key_string_hacker) + hacker_verify_key = SyftVerifyKey.from_string(test_verify_key_string_hacker) res = mongo_store_partition.init_store() assert res.is_ok() @@ -798,7 +798,7 @@ def test_mongo_store_partition_has_permission( obj = MockSyftObject(data=1) permission_root = permission(uid=obj.id, credentials=root_verify_key) permission_client = permission(uid=obj.id, credentials=guest_verify_key) - permission_hacker = permission(uid=obj.id, credentials=hacker_key) + permission_hacker = permission(uid=obj.id, credentials=hacker_verify_key) mongo_store_partition.add_permission(permission_root) # only the root user has access to this permission assert mongo_store_partition.has_permission(permission_root) @@ -809,7 +809,7 @@ def test_mongo_store_partition_has_permission( obj_2 = MockSyftObject(data=2) permission_client_2 = permission(uid=obj_2.id, credentials=guest_verify_key) permission_root_2 = permission(uid=obj_2.id, credentials=root_verify_key) - permisson_hacker_2 = permission(uid=obj_2.id, credentials=hacker_key) + permisson_hacker_2 = permission(uid=obj_2.id, credentials=hacker_verify_key) mongo_store_partition.add_permission(permission_client_2) # the root (admin) and guest client should have this permission assert mongo_store_partition.has_permission(permission_root_2) @@ -838,7 +838,7 @@ def test_mongo_store_partition_take_ownership( mongo_store_partition: MongoStorePartition, permission: ActionObjectPermission, ) -> None: - hacker_key = SyftVerifyKey.from_string(test_verify_key_string_hacker) + hacker_verify_key = SyftVerifyKey.from_string(test_verify_key_string_hacker) obj = MockSyftObject(data=1) # the guest client takes ownership of obj @@ -851,11 +851,13 @@ def test_mongo_store_partition_take_ownership( permission(uid=obj.id, credentials=root_verify_key) ) assert not mongo_store_partition.has_permission( - permission(uid=obj.id, credentials=hacker_key) + permission(uid=obj.id, credentials=hacker_verify_key) ) # hacker or root try to take ownership of the obj and will fail - res = mongo_store_partition.take_ownership(uid=obj.id, credentials=hacker_key) + res = mongo_store_partition.take_ownership( + uid=obj.id, credentials=hacker_verify_key + ) res_2 = mongo_store_partition.take_ownership( uid=obj.id, credentials=root_verify_key ) @@ -874,7 +876,7 @@ def test_mongo_store_partition_take_ownership( permission(uid=obj_2.id, credentials=guest_verify_key) ) assert not mongo_store_partition.has_permission( - permission(uid=obj_2.id, credentials=hacker_key) + permission(uid=obj_2.id, credentials=hacker_verify_key) ) @@ -882,11 +884,14 @@ def test_mongo_store_partition_take_ownership( sys.platform == "win32", reason="pytest_mock_resources + docker issues on Windows" ) def test_mongo_store_partition_permissions_set( - root_verify_key: SyftVerifyKey, mongo_store_partition: MongoStorePartition + root_verify_key: SyftVerifyKey, + guest_verify_key: SyftVerifyKey, + mongo_store_partition: MongoStorePartition, ) -> None: """ Test the permissions functionalities when using MongoStorePartition._set function """ + hacker_verify_key = SyftVerifyKey.from_string(test_verify_key_string_hacker) res = mongo_store_partition.init_store() assert res.is_ok() @@ -903,7 +908,23 @@ def test_mongo_store_partition_permissions_set( permissions = pemissions_collection.find_one({"_id": obj.id}) assert permissions is not None assert isinstance(permissions["permissions"], Set) + # check if the object has been claimed by the root client assert len(permissions["permissions"]) == 4 + for permission in PERMISSIONS: + assert mongo_store_partition.has_permission( + permission(uid=obj.id, credentials=root_verify_key) + ) + + # the hacker tries to set duplicated object but should not be able to claim it + res_2 = mongo_store_partition.set(guest_verify_key, obj, ignore_duplicates=True) + assert res_2.is_ok() + for permission in PERMISSIONS: + assert not mongo_store_partition.has_permission( + permission(uid=obj.id, credentials=hacker_verify_key) + ) + assert mongo_store_partition.has_permission( + permission(uid=obj.id, credentials=root_verify_key) + ) @pytest.mark.skip(reason="To be implemented") From 19ec49112f81c2d9d7fcd30b3f77219809362eda Mon Sep 17 00:00:00 2001 From: khoaguin Date: Thu, 14 Sep 2023 11:08:50 +0700 Subject: [PATCH 10/15] testing permissions when getting all objects for several clients and setting an object when it already exists --- .../syft/stores/mongo_document_store_test.py | 67 +++++++++++++++++-- 1 file changed, 60 insertions(+), 7 deletions(-) diff --git a/packages/syft/tests/syft/stores/mongo_document_store_test.py b/packages/syft/tests/syft/stores/mongo_document_store_test.py index de8caa20fac..759f24fbd99 100644 --- a/packages/syft/tests/syft/stores/mongo_document_store_test.py +++ b/packages/syft/tests/syft/stores/mongo_document_store_test.py @@ -901,14 +901,13 @@ def test_mongo_store_partition_permissions_set( assert res.is_ok() assert res.ok() == obj - # check if the corresponding permissions has been added - # to the mongo_store_partition.permissions collection + # check if the corresponding permissions has been added to the permissions + # collection after the root client claim it pemissions_collection = mongo_store_partition.permissions.ok() assert isinstance(pemissions_collection, MongoCollection) permissions = pemissions_collection.find_one({"_id": obj.id}) assert permissions is not None assert isinstance(permissions["permissions"], Set) - # check if the object has been claimed by the root client assert len(permissions["permissions"]) == 4 for permission in PERMISSIONS: assert mongo_store_partition.has_permission( @@ -927,9 +926,63 @@ def test_mongo_store_partition_permissions_set( ) -@pytest.mark.skip(reason="To be implemented") -def test_mongo_store_partition_permissions_update() -> None: - return None +@pytest.mark.skipif( + sys.platform == "win32", reason="pytest_mock_resources + docker issues on Windows" +) +@pytest.mark.skip(reason="The logic in ._set is a bit confusing. Need to be verified!") +def test_mongo_store_partition_set_permission_exists( + root_verify_key: SyftVerifyKey, + guest_verify_key: SyftVerifyKey, + mongo_store_partition: MongoStorePartition, +) -> None: + """ + Test the permissions functionalities when using MongoStorePartition._set function + when an object already exists and a guest has permission to write it + """ + obj = MockSyftObject(data=1) + # root client sets the object and claim ownership of it + mongo_store_partition.set( + credentials=root_verify_key, obj=obj, ignore_duplicates=False + ) + # the guest client was given WRITE permission to it + mongo_store_partition.add_permission( + ActionObjectWRITE(uid=obj.id, credentials=guest_verify_key) + ) + # guest client sets the object with `ignore_duplicates=True` + mongo_store_partition.set( + credentials=guest_verify_key, obj=obj, ignore_duplicates=True + ) + + +@pytest.mark.skipif( + sys.platform == "win32", reason="pytest_mock_resources + docker issues on Windows" +) +def test_mongo_store_partition_permissions_get_all( + root_verify_key: SyftVerifyKey, + guest_verify_key: SyftVerifyKey, + mongo_store_partition: MongoStorePartition, +) -> None: + hacker_verify_key = SyftVerifyKey.from_string(test_verify_key_string_hacker) + # set several objects for the root and guest client + num_root_objects: int = 5 + num_guest_objects: int = 3 + for i in range(num_root_objects): + obj = MockSyftObject(data=i) + mongo_store_partition.set( + credentials=root_verify_key, obj=obj, ignore_duplicates=False + ) + for i in range(num_guest_objects): + obj = MockSyftObject(data=i) + mongo_store_partition.set( + credentials=guest_verify_key, obj=obj, ignore_duplicates=False + ) + + assert ( + len(mongo_store_partition.all(root_verify_key).ok()) + == num_root_objects + num_guest_objects + ) + assert len(mongo_store_partition.all(guest_verify_key).ok()) == num_guest_objects + assert len(mongo_store_partition.all(hacker_verify_key).ok()) == 0 @pytest.mark.skip(reason="To be implemented") @@ -938,5 +991,5 @@ def test_mongo_store_partition_permissions_delete() -> None: @pytest.mark.skip(reason="To be implemented") -def test_mongo_store_partition_permissions_get_all() -> None: +def test_mongo_store_partition_permissions_update() -> None: return None From bb5675d4476f998eb16cb09ac8625c33fe5bf4ba Mon Sep 17 00:00:00 2001 From: khoaguin Date: Thu, 14 Sep 2023 11:40:44 +0700 Subject: [PATCH 11/15] test permissions when delete objects for several clients --- .../syft/stores/mongo_document_store_test.py | 40 +++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/packages/syft/tests/syft/stores/mongo_document_store_test.py b/packages/syft/tests/syft/stores/mongo_document_store_test.py index 759f24fbd99..d57d2eec2de 100644 --- a/packages/syft/tests/syft/stores/mongo_document_store_test.py +++ b/packages/syft/tests/syft/stores/mongo_document_store_test.py @@ -21,6 +21,7 @@ from syft.service.action.action_store import ActionObjectREAD from syft.service.action.action_store import ActionObjectWRITE from syft.store.document_store import PartitionSettings +from syft.store.document_store import QueryKey from syft.store.document_store import QueryKeys from syft.store.mongo_client import MongoStoreClientConfig from syft.store.mongo_document_store import MongoStoreConfig @@ -985,9 +986,42 @@ def test_mongo_store_partition_permissions_get_all( assert len(mongo_store_partition.all(hacker_verify_key).ok()) == 0 -@pytest.mark.skip(reason="To be implemented") -def test_mongo_store_partition_permissions_delete() -> None: - return None +def test_mongo_store_partition_permissions_delete( + root_verify_key: SyftVerifyKey, + guest_verify_key: SyftVerifyKey, + mongo_store_partition: MongoStorePartition, +) -> None: + hacker_verify_key = SyftVerifyKey.from_string(test_verify_key_string_hacker) + + # the root client set an object + obj = MockSyftObject(data=1) + mongo_store_partition.set( + credentials=root_verify_key, obj=obj, ignore_duplicates=False + ) + qk: QueryKey = mongo_store_partition.settings.unique_keys.with_obj(obj).all[0] + # only the root client can delete it + assert not mongo_store_partition.delete(guest_verify_key, qk).is_ok() + assert not mongo_store_partition.delete(hacker_verify_key, qk).is_ok() + assert mongo_store_partition.delete(root_verify_key, qk).is_ok() + + # the guest client set an object + obj_2 = MockSyftObject(data=2) + mongo_store_partition.set( + credentials=guest_verify_key, obj=obj_2, ignore_duplicates=False + ) + qk_2: QueryKey = mongo_store_partition.settings.unique_keys.with_obj(obj_2).all[0] + # the guest client can delete it + assert not mongo_store_partition.delete(hacker_verify_key, qk_2).is_ok() + assert mongo_store_partition.delete(guest_verify_key, qk_2).is_ok() + + # the guest client set another object + obj_3 = MockSyftObject(data=3) + mongo_store_partition.set( + credentials=guest_verify_key, obj=obj_3, ignore_duplicates=False + ) + qk_3: QueryKey = mongo_store_partition.settings.unique_keys.with_obj(obj_3).all[0] + # the root client also has the permission to delete it + assert mongo_store_partition.delete(root_verify_key, qk_3).is_ok() @pytest.mark.skip(reason="To be implemented") From 93fd6fa28cf483d8722543ea0baa97e88871c107 Mon Sep 17 00:00:00 2001 From: khoaguin Date: Thu, 14 Sep 2023 13:38:47 +0700 Subject: [PATCH 12/15] Remove unnecsessary test Add todo to refactor `MongoStorePartition._set` later Co-authored-by: Shubham Gupta --- .../src/syft/store/mongo_document_store.py | 2 ++ .../syft/stores/mongo_document_store_test.py | 31 ++----------------- 2 files changed, 5 insertions(+), 28 deletions(-) diff --git a/packages/syft/src/syft/store/mongo_document_store.py b/packages/syft/src/syft/store/mongo_document_store.py index 78e8e99f509..b9f3381a670 100644 --- a/packages/syft/src/syft/store/mongo_document_store.py +++ b/packages/syft/src/syft/store/mongo_document_store.py @@ -230,6 +230,8 @@ def _set( add_permissions: Optional[List[ActionObjectPermission]] = None, ignore_duplicates: bool = False, ) -> Result[SyftObject, str]: + # TODO: Refactor this function since now it's doing both set and + # update at the same time write_permission = ActionObjectWRITE(uid=obj.id, credentials=credentials) can_write = self.has_permission(write_permission) diff --git a/packages/syft/tests/syft/stores/mongo_document_store_test.py b/packages/syft/tests/syft/stores/mongo_document_store_test.py index d57d2eec2de..43c9468d854 100644 --- a/packages/syft/tests/syft/stores/mongo_document_store_test.py +++ b/packages/syft/tests/syft/stores/mongo_document_store_test.py @@ -927,34 +927,6 @@ def test_mongo_store_partition_permissions_set( ) -@pytest.mark.skipif( - sys.platform == "win32", reason="pytest_mock_resources + docker issues on Windows" -) -@pytest.mark.skip(reason="The logic in ._set is a bit confusing. Need to be verified!") -def test_mongo_store_partition_set_permission_exists( - root_verify_key: SyftVerifyKey, - guest_verify_key: SyftVerifyKey, - mongo_store_partition: MongoStorePartition, -) -> None: - """ - Test the permissions functionalities when using MongoStorePartition._set function - when an object already exists and a guest has permission to write it - """ - obj = MockSyftObject(data=1) - # root client sets the object and claim ownership of it - mongo_store_partition.set( - credentials=root_verify_key, obj=obj, ignore_duplicates=False - ) - # the guest client was given WRITE permission to it - mongo_store_partition.add_permission( - ActionObjectWRITE(uid=obj.id, credentials=guest_verify_key) - ) - # guest client sets the object with `ignore_duplicates=True` - mongo_store_partition.set( - credentials=guest_verify_key, obj=obj, ignore_duplicates=True - ) - - @pytest.mark.skipif( sys.platform == "win32", reason="pytest_mock_resources + docker issues on Windows" ) @@ -986,6 +958,9 @@ def test_mongo_store_partition_permissions_get_all( assert len(mongo_store_partition.all(hacker_verify_key).ok()) == 0 +@pytest.mark.skipif( + sys.platform == "win32", reason="pytest_mock_resources + docker issues on Windows" +) def test_mongo_store_partition_permissions_delete( root_verify_key: SyftVerifyKey, guest_verify_key: SyftVerifyKey, From 794fe9bff6747be7a0e6bccb1e68007a7673a05d Mon Sep 17 00:00:00 2001 From: khoaguin Date: Thu, 14 Sep 2023 17:50:47 +0700 Subject: [PATCH 13/15] test permissions when updating an object --- .../syft/stores/mongo_document_store_test.py | 50 ++++++++++++++++--- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/packages/syft/tests/syft/stores/mongo_document_store_test.py b/packages/syft/tests/syft/stores/mongo_document_store_test.py index 43c9468d854..556702633ff 100644 --- a/packages/syft/tests/syft/stores/mongo_document_store_test.py +++ b/packages/syft/tests/syft/stores/mongo_document_store_test.py @@ -839,6 +839,9 @@ def test_mongo_store_partition_take_ownership( mongo_store_partition: MongoStorePartition, permission: ActionObjectPermission, ) -> None: + res = mongo_store_partition.init_store() + assert res.is_ok() + hacker_verify_key = SyftVerifyKey.from_string(test_verify_key_string_hacker) obj = MockSyftObject(data=1) @@ -935,6 +938,8 @@ def test_mongo_store_partition_permissions_get_all( guest_verify_key: SyftVerifyKey, mongo_store_partition: MongoStorePartition, ) -> None: + res = mongo_store_partition.init_store() + assert res.is_ok() hacker_verify_key = SyftVerifyKey.from_string(test_verify_key_string_hacker) # set several objects for the root and guest client num_root_objects: int = 5 @@ -966,6 +971,8 @@ def test_mongo_store_partition_permissions_delete( guest_verify_key: SyftVerifyKey, mongo_store_partition: MongoStorePartition, ) -> None: + res = mongo_store_partition.init_store() + assert res.is_ok() hacker_verify_key = SyftVerifyKey.from_string(test_verify_key_string_hacker) # the root client set an object @@ -973,7 +980,7 @@ def test_mongo_store_partition_permissions_delete( mongo_store_partition.set( credentials=root_verify_key, obj=obj, ignore_duplicates=False ) - qk: QueryKey = mongo_store_partition.settings.unique_keys.with_obj(obj).all[0] + qk: QueryKey = mongo_store_partition.settings.store_key.with_obj(obj) # only the root client can delete it assert not mongo_store_partition.delete(guest_verify_key, qk).is_ok() assert not mongo_store_partition.delete(hacker_verify_key, qk).is_ok() @@ -984,7 +991,7 @@ def test_mongo_store_partition_permissions_delete( mongo_store_partition.set( credentials=guest_verify_key, obj=obj_2, ignore_duplicates=False ) - qk_2: QueryKey = mongo_store_partition.settings.unique_keys.with_obj(obj_2).all[0] + qk_2: QueryKey = mongo_store_partition.settings.store_key.with_obj(obj_2) # the guest client can delete it assert not mongo_store_partition.delete(hacker_verify_key, qk_2).is_ok() assert mongo_store_partition.delete(guest_verify_key, qk_2).is_ok() @@ -994,11 +1001,42 @@ def test_mongo_store_partition_permissions_delete( mongo_store_partition.set( credentials=guest_verify_key, obj=obj_3, ignore_duplicates=False ) - qk_3: QueryKey = mongo_store_partition.settings.unique_keys.with_obj(obj_3).all[0] + qk_3: QueryKey = mongo_store_partition.settings.store_key.with_obj(obj_3) # the root client also has the permission to delete it assert mongo_store_partition.delete(root_verify_key, qk_3).is_ok() -@pytest.mark.skip(reason="To be implemented") -def test_mongo_store_partition_permissions_update() -> None: - return None +@pytest.mark.skipif( + sys.platform == "win32", reason="pytest_mock_resources + docker issues on Windows" +) +def test_mongo_store_partition_permissions_update( + root_verify_key: SyftVerifyKey, + guest_verify_key: SyftVerifyKey, + mongo_store_partition: MongoStorePartition, +) -> None: + res = mongo_store_partition.init_store() + assert res.is_ok() + # the root client set an object + obj = MockSyftObject(data=1) + mongo_store_partition.set( + credentials=root_verify_key, obj=obj, ignore_duplicates=False + ) + assert len(mongo_store_partition.all(credentials=root_verify_key).ok()) == 1 + + qk: QueryKey = mongo_store_partition.settings.store_key.with_obj(obj) + permsissions: MongoCollection = mongo_store_partition.permissions.ok() + + for v in range(REPEATS): + # the guest client should not have permission to update obj + obj_new = MockSyftObject(data=v) + res = mongo_store_partition.update( + credentials=guest_verify_key, qk=qk, obj=obj_new + ) + assert res.is_err() + # the root client has the permission to update obj + res = mongo_store_partition.update( + credentials=root_verify_key, qk=qk, obj=obj_new + ) + assert res.is_ok() + # the id of the object in the permission collection should not be changed + assert permsissions.find_one(qk.as_dict_mongo)["_id"] == obj.id From 891ece78f26d9c29d968d7768f80584c35838110 Mon Sep 17 00:00:00 2001 From: khoaguin Date: Fri, 15 Sep 2023 16:48:18 +0700 Subject: [PATCH 14/15] when delete an object, also delete its permission (for MongoStorePartition) add test for this functionality --- .../src/syft/store/mongo_document_store.py | 34 ++++++++++++++----- .../syft/stores/mongo_document_store_test.py | 15 ++++++-- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/packages/syft/src/syft/store/mongo_document_store.py b/packages/syft/src/syft/store/mongo_document_store.py index b9f3381a670..05b5a94d16e 100644 --- a/packages/syft/src/syft/store/mongo_document_store.py +++ b/packages/syft/src/syft/store/mongo_document_store.py @@ -382,21 +382,37 @@ def _get_all_from_store( def _delete( self, credentials: SyftVerifyKey, qk: QueryKey, has_permission: bool = False ) -> Result[SyftSuccess, Err]: + if not ( + has_permission + or self.has_permission( + ActionObjectWRITE(uid=qk.value, credentials=credentials) + ) + ): + return Err(f"You don't have permission to delete object with qk: {qk}") + collection_status = self.collection if collection_status.is_err(): return collection_status collection: MongoCollection = collection_status.ok() - if has_permission or self.has_permission( - ActionObjectWRITE(uid=qk.value, credentials=credentials) - ): - qks = QueryKeys(qks=qk) - result = collection.delete_one(filter=qks.as_dict_mongo) - - if result.deleted_count == 1: - return Ok(SyftSuccess(message="Deleted")) + collection_permissions_status = self.permissions + if collection_permissions_status.is_err(): + return collection_permissions_status + collection_permissions: MongoCollection = collection_permissions_status.ok() - return Err(f"Failed to delete object with qk: {qk}") + qks = QueryKeys(qks=qk) + # delete the object + result = collection.delete_one(filter=qks.as_dict_mongo) + # delete the object's permission + result_permission = collection_permissions.delete_one(filter=qks.as_dict_mongo) + if result.deleted_count == 1 and result_permission.deleted_count == 1: + return Ok(SyftSuccess(message="Object and its permission are deleted")) + elif result.deleted_count == 0: + return Err(f"Failed to delete object with qk: {qk}") + else: + return Err( + f"Object with qk: {qk} was deleted, but failed to delete its corresponding permission" + ) def has_permission(self, permission: ActionObjectPermission) -> bool: """Check if the permission is inside the permission collection""" diff --git a/packages/syft/tests/syft/stores/mongo_document_store_test.py b/packages/syft/tests/syft/stores/mongo_document_store_test.py index 556702633ff..599d387bb9e 100644 --- a/packages/syft/tests/syft/stores/mongo_document_store_test.py +++ b/packages/syft/tests/syft/stores/mongo_document_store_test.py @@ -973,6 +973,8 @@ def test_mongo_store_partition_permissions_delete( ) -> None: res = mongo_store_partition.init_store() assert res.is_ok() + collection: MongoCollection = mongo_store_partition.collection.ok() + pemissions_collection: MongoCollection = mongo_store_partition.permissions.ok() hacker_verify_key = SyftVerifyKey.from_string(test_verify_key_string_hacker) # the root client set an object @@ -981,10 +983,14 @@ def test_mongo_store_partition_permissions_delete( credentials=root_verify_key, obj=obj, ignore_duplicates=False ) qk: QueryKey = mongo_store_partition.settings.store_key.with_obj(obj) - # only the root client can delete it + # guest or hacker can't delete it assert not mongo_store_partition.delete(guest_verify_key, qk).is_ok() assert not mongo_store_partition.delete(hacker_verify_key, qk).is_ok() + # only the root client can delete it assert mongo_store_partition.delete(root_verify_key, qk).is_ok() + # check if the object and its permission have been deleted + assert collection.count_documents({}) == 0 + assert pemissions_collection.count_documents({}) == 0 # the guest client set an object obj_2 = MockSyftObject(data=2) @@ -992,9 +998,12 @@ def test_mongo_store_partition_permissions_delete( credentials=guest_verify_key, obj=obj_2, ignore_duplicates=False ) qk_2: QueryKey = mongo_store_partition.settings.store_key.with_obj(obj_2) - # the guest client can delete it + # the hacker can't delete it assert not mongo_store_partition.delete(hacker_verify_key, qk_2).is_ok() + # the guest client can delete it assert mongo_store_partition.delete(guest_verify_key, qk_2).is_ok() + assert collection.count_documents({}) == 0 + assert pemissions_collection.count_documents({}) == 0 # the guest client set another object obj_3 = MockSyftObject(data=3) @@ -1004,6 +1013,8 @@ def test_mongo_store_partition_permissions_delete( qk_3: QueryKey = mongo_store_partition.settings.store_key.with_obj(obj_3) # the root client also has the permission to delete it assert mongo_store_partition.delete(root_verify_key, qk_3).is_ok() + assert collection.count_documents({}) == 0 + assert pemissions_collection.count_documents({}) == 0 @pytest.mark.skipif( From 89359b27ff098c60ed5b91b84cbdc1c5aa96900c Mon Sep 17 00:00:00 2001 From: khoaguin Date: Fri, 15 Sep 2023 17:38:55 +0700 Subject: [PATCH 15/15] delete corresponding permisisons after delete an object for KeyValueStorePartition --- packages/syft/src/syft/store/kv_document_store.py | 1 + packages/syft/tests/syft/stores/kv_document_store_test.py | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/syft/src/syft/store/kv_document_store.py b/packages/syft/src/syft/store/kv_document_store.py index d7131f52be2..bc1c6d2ea37 100644 --- a/packages/syft/src/syft/store/kv_document_store.py +++ b/packages/syft/src/syft/store/kv_document_store.py @@ -450,6 +450,7 @@ def _delete( ActionObjectWRITE(uid=qk.value, credentials=credentials) ): _obj = self.data.pop(qk.value) + self.permissions.pop(qk.value) self._delete_unique_keys_for(_obj) self._delete_search_keys_for(_obj) return Ok(SyftSuccess(message="Deleted")) diff --git a/packages/syft/tests/syft/stores/kv_document_store_test.py b/packages/syft/tests/syft/stores/kv_document_store_test.py index 956a5077a7e..3e66d753a96 100644 --- a/packages/syft/tests/syft/stores/kv_document_store_test.py +++ b/packages/syft/tests/syft/stores/kv_document_store_test.py @@ -101,7 +101,7 @@ def test_kv_store_partition_delete( assert len(kv_store_partition.all(root_verify_key).ok()) == len(objs) - # random object + # can't delete a random object since it was not added obj = MockSyftObject(data="bogus") key = kv_store_partition.settings.store_key.with_obj(obj) res = kv_store_partition.delete(root_verify_key, key) @@ -114,10 +114,13 @@ def test_kv_store_partition_delete( res = kv_store_partition.delete(root_verify_key, key) assert res.is_ok() assert len(kv_store_partition.all(root_verify_key).ok()) == len(objs) - idx - 1 + # check that the corresponding permissions were also deleted + assert len(kv_store_partition.data) == len(kv_store_partition.permissions) res = kv_store_partition.delete(root_verify_key, key) assert res.is_err() assert len(kv_store_partition.all(root_verify_key).ok()) == len(objs) - idx - 1 + assert len(kv_store_partition.data) == len(kv_store_partition.permissions) assert len(kv_store_partition.all(root_verify_key).ok()) == 0