Skip to content

Commit

Permalink
Merge pull request #9052 from OpenMined/feature/reset_password_feature
Browse files Browse the repository at this point in the history
ADD reset password api
  • Loading branch information
IonesioJunior authored Jul 31, 2024
2 parents 20e6c5f + c19853e commit 56c4caf
Show file tree
Hide file tree
Showing 13 changed files with 902 additions and 13 deletions.
181 changes: 181 additions & 0 deletions notebooks/api/0.8/13-forgot-user-password.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "0",
"metadata": {},
"source": [
"# Forgot User Password"
]
},
{
"cell_type": "markdown",
"id": "1",
"metadata": {},
"source": [
"## Initialize the server"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2",
"metadata": {},
"outputs": [],
"source": [
"# stdlib\n",
"\n",
"# syft absolute\n",
"import syft as sy\n",
"from syft import SyftError\n",
"from syft import SyftSuccess\n",
"\n",
"server = sy.orchestra.launch(\n",
" name=\"test-datasite-1\",\n",
" dev_mode=True,\n",
" create_producer=True,\n",
" n_consumers=3,\n",
" reset=True,\n",
" port=8081,\n",
")"
]
},
{
"cell_type": "markdown",
"id": "3",
"metadata": {},
"source": [
"## Register a new user"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4",
"metadata": {},
"outputs": [],
"source": [
"datasite_client = server.login(email=\"[email protected]\", password=\"changethis\")\n",
"res = datasite_client.register(\n",
" email=\"[email protected]\",\n",
" password=\"verysecurepassword\",\n",
" password_verify=\"verysecurepassword\",\n",
" name=\"New User\",\n",
")\n",
"\n",
"if not isinstance(res, SyftSuccess):\n",
" raise Exception(f\"Res isn't SyftSuccess, its {res}\")"
]
},
{
"cell_type": "markdown",
"id": "5",
"metadata": {},
"source": [
"### Ask for a password reset - Notifier disabled Workflow"
]
},
{
"cell_type": "markdown",
"id": "6",
"metadata": {},
"source": [
"### Call for users.forgot_password"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7",
"metadata": {},
"outputs": [],
"source": [
"guest_client = server.login_as_guest()\n",
"res = guest_client.users.forgot_password(email=\"[email protected]\")\n",
"\n",
"if not isinstance(res, SyftSuccess):\n",
" raise Exception(f\"Res isn't SyftSuccess, its {res}\")"
]
},
{
"cell_type": "markdown",
"id": "8",
"metadata": {},
"source": [
"### Admin generates a temp token"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9",
"metadata": {},
"outputs": [],
"source": [
"temp_token = datasite_client.users.request_password_reset(\n",
" datasite_client.notifications[-1].linked_obj.resolve.id\n",
")\n",
"\n",
"if not isinstance(temp_token, str):\n",
" raise Exception(f\"temp_token isn't a string, its {temp_token}\")"
]
},
{
"cell_type": "markdown",
"id": "10",
"metadata": {},
"source": [
"### User use this token to reset password"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "11",
"metadata": {},
"outputs": [],
"source": [
"res = guest_client.users.reset_password(token=temp_token, new_password=\"Password123\")\n",
"\n",
"if not isinstance(res, SyftSuccess):\n",
" raise Exception(f\"Res isn't SyftSuccess, its {res}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "12",
"metadata": {},
"outputs": [],
"source": [
"new_user_session = server.login(\n",
" email=\"[email protected]\", password=\"Password123\"\n",
")\n",
"\n",
"if isinstance(new_user_session, SyftError):\n",
" raise Exception(f\"Res isn't SyftSuccess, its {new_user_session}\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.4"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
46 changes: 46 additions & 0 deletions packages/syft/src/syft/protocol/protocol_version.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,51 @@
},
"2": {
"release_name": "0.8.8.json"
},
"dev": {
"object_versions": {
"User": {
"2": {
"version": 2,
"hash": "af6fb5b2e1606e97838f4a60f0536ad95db606d455e94acbd1977df866608a2c",
"action": "add"
}
},
"UserNotificationActivity": {
"1": {
"version": 1,
"hash": "422fd01c6d9af38688a9982abd34e80794a1f6ddd444cca225d77f49189847a9",
"action": "add"
}
},
"NotifierSettings": {
"2": {
"version": 2,
"hash": "be8b52597fc628d1b7cd22b776ee81416e1adbb04a45188778eb0e32ed1416b4",
"action": "add"
}
},
"PwdTokenResetConfig": {
"1": {
"version": 1,
"hash": "0415a272428f22add4896c64aa9f29c8c1d35619e2433da6564eb5f1faff39ac",
"action": "add"
}
},
"ServerSettingsUpdate": {
"3": {
"version": 3,
"hash": "335c7946f2e52d09c7b26f511120cd340717c74c5cca9107e84f839da993c55c",
"action": "add"
}
},
"ServerSettings": {
"3": {
"version": 3,
"hash": "997667e1cba22d151857aacc2caba6b1ca73c1648adbd03461dc74a0c0c372b3",
"action": "add"
}
}
}
}
}
86 changes: 86 additions & 0 deletions packages/syft/src/syft/service/notification/email_templates.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# stdlib
from datetime import datetime
from typing import TYPE_CHECKING
from typing import cast

Expand All @@ -22,6 +23,91 @@ def email_body(notification: "Notification", context: AuthedServiceContext) -> s
return ""


@serializable(canonical_name="PasswordResetTemplate", version=1)
class PasswordResetTemplate(EmailTemplate):
@staticmethod
def email_title(notification: "Notification", context: AuthedServiceContext) -> str:
return "Password Reset Requested"

@staticmethod
def email_body(notification: "Notification", context: AuthedServiceContext) -> str:
user_service = context.server.get_service("userservice")

user = user_service.get_by_verify_key(notification.to_user_verify_key)
if not user:
raise Exception("User not found!")

user.reset_token = user_service.generate_new_password_reset_token(
context.server.settings.pwd_token_config
)
user.reset_token_date = datetime.now()

result = user_service.stash.update(
credentials=context.credentials, user=user, has_permission=True
)
if result.is_err():
raise Exception("Couldn't update the user password")

head = """<head>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
color: #333;
margin: 0;
padding: 0;
}
.container {
max-width: 600px;
margin: 50px auto;
background-color: #ffffff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
h1 {
color: #333;
text-align: center;
}
p {
font-size: 16px;
line-height: 1.5;
}
.button {
display: block;
width: 200px;
margin: 30px auto;
padding: 10px;
background-color: #007BFF;
color: #fff;
text-align: center;
text-decoration: none;
border-radius: 4px;
}
.footer {
text-align: center;
font-size: 12px;
color: #aaa;
margin-top: 20px;
}
</style>
</head>"""
body = f"""<body>
<div class="container">
<h1>Password Reset</h1>
<p>We received a request to reset your password. Your new temporary token is:</p>
<h1>{user.reset_token}</h1>
<p> Use
<code style="color: #FF8C00;background-color: #f0f0f0;font-size: 12px;">
syft_client.reset_password(token='{user.reset_token}', new_password=*****)
</code>.
to reset your password.</p>
<p>If you didn't request a password reset, please ignore this email.</p>
</div>
</body>"""
return f"""<html>{head} {body}</html>"""


@serializable(canonical_name="OnboardEmailTemplate", version=1)
class OnBoardEmailTemplate(EmailTemplate):
@staticmethod
Expand Down
Loading

0 comments on commit 56c4caf

Please sign in to comment.