Skip to content

Commit

Permalink
Partial implementation of enable/disable User Notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
jcardonnet committed Feb 26, 2024
1 parent f348928 commit 6f0d664
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 74 deletions.
42 changes: 4 additions & 38 deletions packages/syft/src/syft/service/notifier/notifier_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
from ...store.document_store import DocumentStore
from ..context import AuthedServiceContext
from ..notification.notifications import Notification

# from ..user.user import User
from ..response import SyftError
from ..response import SyftSuccess
from ..service import AbstractService
Expand Down Expand Up @@ -161,32 +163,8 @@ def activate(
This will only work if the domain owner has enabled notifications.
"""

# TODO: user credentials should not be used to get the notifier settings
# TODO: WARNING THIS IS A POTENTIAL SECURITY RISK (ONLY FOR DEVELOPMENT PURPOSES)

admin_key = self.stash.admin_verify_key()

result = self.stash.get(credentials=admin_key)
print(result)

if result.is_err():
return SyftError(message=result.err())

notifier = result.ok()

user_key = context.credentials

if user_key in notifier.email_subscribers:
return SyftSuccess(
message="Notifications are already activated for this user."
)
# TODO: User method in User Service to disable notifications

notifier.email_subscribers.add(user_key)

# TODO: user credentials should not be used to update the notifier settings
result = self.stash.update(credentials=admin_key, settings=notifier)
if result.is_err():
return SyftError(message=result.err())
return SyftSuccess(message="Notifications enabled successfully.")

@service_method(
Expand All @@ -202,20 +180,8 @@ def deactivate(
This will only work if the domain owner has enabled notifications.
"""

# TODO: WARNING THIS IS A POTENTIAL SECURITY RISK (ONLY FOR DEVELOPMENT PURPOSES)
admin_key = self.stash.admin_verify_key()
result = self.stash.get(credentials=admin_key)

if result.is_err():
return SyftError(message=result.err())

notifier = result.ok()
user_key = context.credentials
notifier.email_subscribers.remove(user_key)
# TODO: User method in User Service to disable notifications

result = self.stash.update(credentials=admin_key, settings=notifier)
if result.is_err():
return SyftError(message=result.err())
return SyftSuccess(message="Notifications disabled successfully.")

@staticmethod
Expand Down
59 changes: 23 additions & 36 deletions packages/syft/src/syft/service/user/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@
from ...types.transforms import transform
from ...types.transforms import validate_email
from ...types.uid import UID
from ..notifier.notifier_enums import NOTIFIERS
from ..response import SyftError
from ..response import SyftSuccess
from .user_roles import ServiceRole
from ..notifier.notifier_enums import NOTIFIERS


@serializable()
class UserV1(SyftObject):
Expand Down Expand Up @@ -84,6 +85,7 @@ class UserV2(SyftObject):
__attr_unique__ = ["email", "signing_key", "verify_key"]
__repr_attrs__ = ["name", "email"]


@serializable()
class User(SyftObject):
# version
Expand Down Expand Up @@ -206,7 +208,6 @@ def str_to_role(cls, v: Any) -> Any:
return getattr(ServiceRole, v.upper())
return v

email_notifications_enabled: Optional[bool] = None
email: EmailStr
name: str
role: ServiceRole # make sure role cant be set without uid
Expand Down Expand Up @@ -258,7 +259,6 @@ class UserCreate(UserUpdate):
__canonical_name__ = "UserCreate"
__version__ = SYFT_OBJECT_VERSION_3

email_notifications_enabled: bool = True
email: EmailStr
name: str
role: Optional[ServiceRole] = None # TODO: type: ignore[assignment]
Expand Down Expand Up @@ -316,7 +316,12 @@ class UserView(SyftObject):
__canonical_name__ = "UserView"
__version__ = SYFT_OBJECT_VERSION_3

email_notifications_enabled: bool
notifications_enabled: Dict[NOTIFIERS, bool] = {
NOTIFIERS.EMAIL: True,
NOTIFIERS.SMS: False,
NOTIFIERS.SLACK: False,
NOTIFIERS.APP: False,
}
email: EmailStr
name: str
role: ServiceRole # make sure role cant be set without uid
Expand All @@ -330,7 +335,7 @@ class UserView(SyftObject):
"institution",
"website",
"role",
"email_notifications_enabled",
"notifications_enabled",
]

def _coll_repr_(self) -> Dict[str, Any]:
Expand All @@ -340,6 +345,10 @@ def _coll_repr_(self) -> Dict[str, Any]:
"Institute": self.institution,
"Website": self.website,
"Role": self.role.name.capitalize(),
"Notifications": "Email: "
+ (
"Enabled" if self.notifications_enabled[NOTIFIERS.EMAIL] else "Disabled"
),
}

def _set_password(self, new_password: str) -> Union[SyftError, SyftSuccess]:
Expand Down Expand Up @@ -481,7 +490,7 @@ def user_to_view_user() -> List[Callable]:
"institution",
"website",
"mock_execution_permission",
"email_notifications_enabled",
"notifications_enabled",
]
)
]
Expand All @@ -502,12 +511,12 @@ def user_to_user_verify() -> List[Callable]:
return [keep(["email", "signing_key", "id", "role"])]


@migrate(UserV1, User)
@migrate(UserV1, UserV2)
def upgrade_user_v1_to_v2() -> List[Callable]:
return [make_set_default(key="mock_execution_permission", value=False)]


@migrate(User, UserV1)
@migrate(UserV2, UserV1)
def downgrade_user_v2_to_v1() -> List[Callable]:
return [drop(["mock_execution_permission"])]

Expand All @@ -532,55 +541,33 @@ def downgrade_user_create_v2_to_v1() -> List[Callable]:
return [drop(["mock_execution_permission"])]


@migrate(UserViewV1, UserView)
@migrate(UserViewV1, UserViewV2)
def upgrade_user_view_v1_to_v2() -> List[Callable]:
return [make_set_default(key="mock_execution_permission", value=False)]


@migrate(UserView, UserViewV1)
@migrate(UserViewV2, UserViewV1)
def downgrade_user_view_v2_to_v1() -> List[Callable]:
return [drop(["mock_execution_permission"])]


# User
@migrate(UserV2, User)
def upgrade_user_v2_to_v3() -> List[Callable]:
return [make_set_default(key="email_notifications_enabled", value=True)]
return [make_set_default(key="notifications_enabled", value=True)]


@migrate(User, UserV2)
def downgrade_user_v3_to_v2() -> List[Callable]:
return [drop(["email_notifications_enabled"])]
return [drop(["notifications_enabled"])]


# View
@migrate(UserViewV2, UserView)
def upgrade_user_view_v2_to_v3() -> List[Callable]:
return [make_set_default(key="email_notifications_enabled", value=True)]
return [make_set_default(key="notifications_enabled", value=True)]


@migrate(UserView, UserViewV2)
def downgrade_user_view_v3_to_v2() -> List[Callable]:
return [drop(["email_notifications_enabled"])]


# Create
@migrate(UserCreateV2, UserCreate)
def upgrade_user_create_v2_to_v3() -> List[Callable]:
return [make_set_default(key="email_notifications_enabled", value=True)]


@migrate(UserView, UserViewV2)
def downgrade_create_create_v3_to_v2() -> List[Callable]:
return [drop(["email_notifications_enabled"])]


# Update
@migrate(UserUpdateV2, UserUpdate)
def upgrade_user_update_v2_to_v3() -> List[Callable]:
return [make_set_default(key="email_notifications_enabled", value=True)]


@migrate(UserUpdate, UserUpdateV2)
def downgrade_user_update_v3_to_v2() -> List[Callable]:
return [drop(["email_notifications_enabled"])]
return [drop(["notifications_enabled"])]
23 changes: 23 additions & 0 deletions packages/syft/src/syft/service/user/user_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,29 @@ def get_by_verify_key(
return result.ok()
return SyftError(message=f"No User with verify_key: {verify_key}")

# TODO: This exposed service is only for the development phase.
# enable/disable notifications will be called from Notifier Service

@service_method(
path="user.enable_notifications",
name="enable_notifications",
roles=GUEST_ROLE_LEVEL,
)
def enable_notifications(
self, context: AuthedServiceContext
) -> Union[UserView, SyftError]:
result = self.stash.get_by_verify_key(
credentials=context.credentials, verify_key=context.credentials
)
if result.is_ok():
# this seems weird that we get back None as Ok(None)
user = result.ok()
if user:
return user
else:
SyftError(message="User not found!")
return SyftError(message=str(result.err()))


TYPE_TO_SERVICE[User] = UserService
SERVICE_TO_TYPES[UserService].update({User})

0 comments on commit 6f0d664

Please sign in to comment.