diff --git a/packages/syft/src/syft/service/notifier/notifier_service.py b/packages/syft/src/syft/service/notifier/notifier_service.py index 7c92d1451b7..4fbcb2d9784 100644 --- a/packages/syft/src/syft/service/notifier/notifier_service.py +++ b/packages/syft/src/syft/service/notifier/notifier_service.py @@ -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 @@ -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( @@ -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 diff --git a/packages/syft/src/syft/service/user/user.py b/packages/syft/src/syft/service/user/user.py index e5160121d0e..4fcdc9d34e2 100644 --- a/packages/syft/src/syft/service/user/user.py +++ b/packages/syft/src/syft/service/user/user.py @@ -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): @@ -84,6 +85,7 @@ class UserV2(SyftObject): __attr_unique__ = ["email", "signing_key", "verify_key"] __repr_attrs__ = ["name", "email"] + @serializable() class User(SyftObject): # version @@ -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 @@ -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] @@ -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 @@ -330,7 +335,7 @@ class UserView(SyftObject): "institution", "website", "role", - "email_notifications_enabled", + "notifications_enabled", ] def _coll_repr_(self) -> Dict[str, Any]: @@ -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]: @@ -481,7 +490,7 @@ def user_to_view_user() -> List[Callable]: "institution", "website", "mock_execution_permission", - "email_notifications_enabled", + "notifications_enabled", ] ) ] @@ -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"])] @@ -532,12 +541,12 @@ 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"])] @@ -545,42 +554,20 @@ def downgrade_user_view_v2_to_v1() -> List[Callable]: # 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"])] diff --git a/packages/syft/src/syft/service/user/user_service.py b/packages/syft/src/syft/service/user/user_service.py index 8bdb28bc275..99e50950bda 100644 --- a/packages/syft/src/syft/service/user/user_service.py +++ b/packages/syft/src/syft/service/user/user_service.py @@ -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})