Skip to content

Commit ba22894

Browse files
authored
Merge pull request #8090 from khoaguin/user-update-bug
Fix the bug "Guest user is able to change email to owner and essentially kills nodes"
2 parents 87f3367 + b7a473f commit ba22894

File tree

3 files changed

+50
-10
lines changed

3 files changed

+50
-10
lines changed

packages/syft/src/syft/service/user/user_service.py

+4-5
Original file line numberDiff line numberDiff line change
@@ -229,13 +229,12 @@ def update(
229229
# Get user to be updated by its UID
230230
result = self.stash.get_by_uid(credentials=context.credentials, uid=uid)
231231

232-
# TODO: ADD Email Validation
233-
# check if the email already exists
232+
# check if the email already exists (with root's key)
234233
if user_update.email is not Empty:
235-
user_with_email = self.stash.get_by_email(
236-
credentials=context.credentials, email=user_update.email
234+
user_with_email_exists: bool = self.stash.email_exists(
235+
email=user_update.email
237236
)
238-
if user_with_email.ok() is not None:
237+
if user_with_email_exists:
239238
return SyftError(
240239
message=f"A user with the email {user_update.email} already exists."
241240
)

packages/syft/src/syft/service/user/user_stash.py

+7
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,13 @@ def get_by_email(
7676
qks = QueryKeys(qks=[EmailPartitionKey.with_obj(email)])
7777
return self.query_one(credentials=credentials, qks=qks)
7878

79+
def email_exists(self, email: str) -> bool:
80+
res = self.get_by_email(credentials=self.admin_verify_key().ok(), email=email)
81+
if res.ok() is None:
82+
return False
83+
else:
84+
return True
85+
7986
def get_by_role(
8087
self, credentials: SyftVerifyKey, role: ServiceRole
8188
) -> Result[Optional[User], str]:

packages/syft/tests/syft/users/user_test.py

+39-5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from syft import SyftSuccess
99
from syft.client.api import SyftAPICall
1010
from syft.client.domain_client import DomainClient
11+
from syft.node.node import get_default_root_email
1112
from syft.node.worker import Worker
1213
from syft.service.context import AuthedServiceContext
1314
from syft.service.user.user import ServiceRole
@@ -32,7 +33,7 @@ def get_users(worker):
3233
)
3334

3435

35-
def get_mock_client(root_client, role):
36+
def get_mock_client(root_client, role) -> DomainClient:
3637
worker = root_client.api.connection.node
3738
client = worker.guest_client
3839
mail = Faker().email()
@@ -66,17 +67,17 @@ def manually_call_service(worker, client, service, args=None, kwargs=None):
6667

6768

6869
@pytest.fixture
69-
def guest_client(worker):
70+
def guest_client(worker) -> DomainClient:
7071
return get_mock_client(worker.root_client, ServiceRole.GUEST)
7172

7273

7374
@pytest.fixture
74-
def ds_client(worker):
75+
def ds_client(worker) -> DomainClient:
7576
return get_mock_client(worker.root_client, ServiceRole.DATA_SCIENTIST)
7677

7778

7879
@pytest.fixture
79-
def do_client(worker):
80+
def do_client(worker) -> DomainClient:
8081
return get_mock_client(worker.root_client, ServiceRole.DATA_OWNER)
8182

8283

@@ -233,6 +234,24 @@ def test_user_update(root_client):
233234
)
234235

235236

237+
def test_guest_user_update_to_root_email_failed(
238+
root_client: DomainClient,
239+
do_client: DomainClient,
240+
guest_client: DomainClient,
241+
ds_client: DomainClient,
242+
) -> None:
243+
default_root_email: str = get_default_root_email()
244+
user_update_to_root_email = UserUpdate(email=default_root_email)
245+
for client in [root_client, do_client, guest_client, ds_client]:
246+
res = client.api.services.user.update(
247+
uid=client.me.id, user_update=user_update_to_root_email
248+
)
249+
assert isinstance(res, SyftError)
250+
assert (
251+
res.message == f"A user with the email {default_root_email} already exists."
252+
)
253+
254+
236255
def test_user_view_set_password(worker: Worker, root_client: DomainClient) -> None:
237256
root_client.me.set_password("123", confirm=False)
238257
email = root_client.me.email
@@ -260,7 +279,6 @@ def test_user_view_set_invalid_email(
260279
[
261280
262281
263-
264282
],
265283
)
266284
def test_user_view_set_email_success(
@@ -275,6 +293,22 @@ def test_user_view_set_email_success(
275293
assert isinstance(result2, SyftSuccess)
276294

277295

296+
def test_user_view_set_default_admin_email_failed(
297+
ds_client: DomainClient, guest_client: DomainClient
298+
) -> None:
299+
default_root_email = get_default_root_email()
300+
result = ds_client.me.set_email(default_root_email)
301+
assert isinstance(result, SyftError)
302+
assert (
303+
result.message == f"A user with the email {default_root_email} already exists."
304+
)
305+
result_2 = guest_client.me.set_email(default_root_email)
306+
assert isinstance(result_2, SyftError)
307+
assert (
308+
result.message == f"A user with the email {default_root_email} already exists."
309+
)
310+
311+
278312
def test_user_view_set_duplicated_email(
279313
root_client: DomainClient, ds_client: DomainClient, guest_client: DomainClient
280314
) -> None:

0 commit comments

Comments
 (0)