Skip to content

Commit c6d38cb

Browse files
authored
Allow email confirmation without being authenticated. (#79)
* Allow email confirmation without being authenticated. * PR fixes for: Allow email confirmation without being authenticated. * PR fixes for: Allow email confirmation without being authenticated.
1 parent 3bca0f0 commit c6d38cb

7 files changed

Lines changed: 108 additions & 25 deletions

File tree

genesis_core/elements/dm/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ class Resource(
317317

318318
def get_uri(self):
319319
parts = self.link.split(".")
320-
return f"/{"/".join(parts[1:-1])}/{self.uuid}"
320+
return f"/{'/'.join(parts[1:-1])}/{self.uuid}"
321321

322322
def get_parameter_value(self, parameter):
323323
parts = parameter.split(":")

genesis_core/elements/dm/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def get_element_uuid(element_name, element_version):
5454
def get_project_id():
5555
# return sys_uuid.UUID(f"{UUID_PREFIX}{str(sys_uuid.uuid4())[8:]}")
5656
return sys_uuid.UUID(
57-
f"{UUID_PREFIX}{str("00000000-0000-0000-0000-000000000000")[8:]}"
57+
f"{UUID_PREFIX}{str('00000000-0000-0000-0000-000000000000')[8:]}"
5858
)
5959

6060

genesis_core/tests/functional/conftest.py

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
from genesis_core.config import constants as cc
3737
from genesis_core.secret import constants as sc
3838
from genesis_core.secret.dm import models as secret_models
39-
39+
from genesis_core.user_api.iam.dm import models as iam_models
4040

4141
FIRST_MIGRATION = "0000-root-d34de1.py"
4242

@@ -136,17 +136,21 @@ def auth_test1_user(
136136
):
137137
password = "test1"
138138
client = user_api_client(auth_user_admin)
139-
result = client.create_user(username="test1", password=password)
140-
client.confirm_email(result["uuid"])
139+
user = client.create_user(username="test1", password=password)
140+
user_obj = iam_models.User.objects.get_one(filters={"uuid": user["uuid"]})
141+
client.confirm_email(
142+
user_uuid=user_obj.uuid,
143+
code=str(user_obj.confirmation_code),
144+
)
141145

142146
return iam_clients.GenesisCoreAuth(
143-
username=result["username"],
147+
username=user["username"],
144148
password=password,
145149
client_uuid=default_client_uuid,
146150
client_id=default_client_id,
147151
client_secret=default_client_secret,
148-
uuid=result["uuid"],
149-
email=result["email"],
152+
uuid=user["uuid"],
153+
email=user["email"],
150154
)
151155

152156

@@ -160,17 +164,21 @@ def auth_test2_user(
160164
):
161165
password = "test2"
162166
client = user_api_client(auth_user_admin)
163-
result = client.create_user(username="test2", password=password)
164-
client.confirm_email(result["uuid"])
167+
user = client.create_user(username="test2", password=password)
168+
user_obj = iam_models.User.objects.get_one(filters={"uuid": user["uuid"]})
169+
client.confirm_email(
170+
user_uuid=user_obj.uuid,
171+
code=str(user_obj.confirmation_code),
172+
)
165173

166174
return iam_clients.GenesisCoreAuth(
167-
username=result["username"],
175+
username=user["username"],
168176
password=password,
169177
client_uuid=default_client_uuid,
170178
client_id=default_client_id,
171179
client_secret=default_client_secret,
172-
uuid=result["uuid"],
173-
email=result["email"],
180+
uuid=user["uuid"],
181+
email=user["email"],
174182
)
175183

176184

@@ -185,7 +193,11 @@ def auth_test1_p1_user(
185193
password = "test1p1"
186194
client = user_api_client(auth_user_admin)
187195
user = client.create_user(username="test1p1", password=password)
188-
client.confirm_email(user_uuid=user["uuid"])
196+
user_obj = iam_models.User.objects.get_one(filters={"uuid": user["uuid"]})
197+
client.confirm_email(
198+
user_uuid=user_obj.uuid,
199+
code=str(user_obj.confirmation_code),
200+
)
189201

190202
auth = iam_clients.GenesisCoreAuth(
191203
username=user["username"],
@@ -198,9 +210,7 @@ def auth_test1_p1_user(
198210
project_id=None,
199211
)
200212

201-
client = client = user_api_client(
202-
auth,
203-
)
213+
client = user_api_client(auth)
204214

205215
org = client.create_organization(name="OrganizationU1P1")
206216
project = client.create_project(
@@ -232,7 +242,11 @@ def auth_test2_p1_user(
232242
password = "test2p1"
233243
client = user_api_client(auth_user_admin)
234244
user = client.create_user(username="test2p1", password=password)
235-
client.confirm_email(user_uuid=user["uuid"])
245+
user_obj = iam_models.User.objects.get_one(filters={"uuid": user["uuid"]})
246+
client.confirm_email(
247+
user_uuid=user_obj.uuid,
248+
code=str(user_obj.confirmation_code),
249+
)
236250

237251
auth = iam_clients.GenesisCoreAuth(
238252
username=user["username"],
@@ -245,9 +259,7 @@ def auth_test2_p1_user(
245259
project_id=None,
246260
)
247261

248-
client = client = user_api_client(
249-
auth,
250-
)
262+
client = user_api_client(auth)
251263

252264
org = client.create_organization(name="OrganizationU2P1")
253265
project = client.create_project(

genesis_core/tests/functional/restapi/iam/test_users.py

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@
1313
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1414
# License for the specific language governing permissions and limitations
1515
# under the License.
16+
import uuid as sys_uuid
1617

17-
from bazooka import exceptions as bazooka_exc
1818
import pytest
19+
from bazooka import exceptions as bazooka_exc
1920

2021
from genesis_core.common import constants as common_c
2122
from genesis_core.tests.functional.restapi.iam import base
2223
from genesis_core.user_api.iam import constants as c
24+
from genesis_core.user_api.iam.dm import models as iam_models
2325

2426

2527
class TestUsers(base.BaseIamResourceTest):
@@ -231,6 +233,57 @@ def test_update_other_user_change_password_test1_auth_success(
231233

232234
assert result["uuid"] == auth_test2_user.uuid
233235

236+
def test_confirm_email_no_auth_success(
237+
self,
238+
user_api_noauth_client,
239+
auth_test1_user,
240+
):
241+
user = iam_models.User.objects.get_one(
242+
filters={"uuid": auth_test1_user.uuid}
243+
)
244+
user.email_verified = False
245+
user.confirmation_code = sys_uuid.uuid4()
246+
user.save()
247+
248+
client = user_api_noauth_client()
249+
result = client.confirm_email(
250+
user_uuid=auth_test1_user.uuid,
251+
code=str(user.confirmation_code),
252+
)
253+
# Check for success in the API response
254+
assert result["uuid"] == str(auth_test1_user.uuid)
255+
assert result["email_verified"] is True
256+
257+
# Check for success in the DB
258+
user_updated = iam_models.User.objects.get_one(
259+
filters={"uuid": auth_test1_user.uuid}
260+
)
261+
assert user_updated.email_verified
262+
263+
def test_confirm_email_invalid_code_400_error(
264+
self,
265+
user_api_noauth_client,
266+
auth_test1_user,
267+
):
268+
client = user_api_noauth_client()
269+
270+
with pytest.raises(bazooka_exc.ForbiddenError):
271+
client.confirm_email(
272+
user_uuid=auth_test1_user.uuid,
273+
code="invalid code",
274+
)
275+
276+
def test_confirm_email_no_code_400_error(
277+
self, user_api_noauth_client, auth_test1_user
278+
):
279+
client = user_api_noauth_client()
280+
281+
with pytest.raises(bazooka_exc.ForbiddenError):
282+
client.confirm_email(
283+
user_uuid=auth_test1_user.uuid,
284+
code=None,
285+
)
286+
234287
def test_delete_my_user_test1_auth_success(
235288
self, user_api_client, auth_test1_user
236289
):

genesis_core/user_api/api/app.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@
5555
"/actions/reset_password/invoke",
5656
methods=[ra_c.POST],
5757
),
58+
iam_mw.EndpointComparator(
59+
f"/v1/iam/users/({ra_types.UUID_RE_TEMPLATE})"
60+
"/actions/confirm_email/invoke",
61+
methods=[ra_c.POST],
62+
),
5863
]
5964

6065

genesis_core/user_api/iam/api/controllers.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,17 @@ def resend_email_confirmation(self, resource):
212212
resource.resend_confirmation_event(app_endpoint=app_endpoint)
213213
return resource
214214

215+
@actions.post
216+
def force_confirm_email(self, resource):
217+
rule = c.PERMISSION_USER_WRITE_ALL
218+
if not self.enforce(rule):
219+
raise iam_e.CanNotUpdateUser(uuid=resource.uuid, rule=rule)
220+
221+
resource.confirm_email()
222+
return resource
223+
215224
@actions.post
216225
def confirm_email(self, resource, code=None):
217-
if self.enforce(c.PERMISSION_USER_WRITE_ALL):
218-
resource.confirm_email()
219-
return resource
220226
code = code or self._req.params.get("code", "")
221227
resource.confirm_email_by_code(code)
222228
return resource

genesis_core/user_api/iam/api/routes.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ class ConfirmEmailAction(routes.Action):
7070
__controller__ = controllers.UserController
7171

7272

73+
class ForceConfirmEmailAction(routes.Action):
74+
"""Handler for .../actions/force_confirm_email/invoke endpoint"""
75+
76+
__controller__ = controllers.UserController
77+
78+
7379
class ResetPasswordAction(routes.Action):
7480
"""Handler for .../actions/reset_password/invoke endpoint"""
7581

@@ -90,6 +96,7 @@ class UserRoute(routes.Route):
9096
ResendEmailConfirmationAction, invoke=True
9197
)
9298
confirm_email = routes.action(ConfirmEmailAction, invoke=True)
99+
force_confirm_email = routes.action(ForceConfirmEmailAction, invoke=True)
93100
reset_password = routes.action(ResetPasswordAction, invoke=True)
94101

95102

0 commit comments

Comments
 (0)