From 9016a41a5a935d1d8229403672623c78d4262d41 Mon Sep 17 00:00:00 2001 From: Daniel Prange Date: Wed, 24 Jul 2024 15:35:40 +0300 Subject: [PATCH] fix: ignore unknown login methods Refs: HP-2490 --- profiles/schema.py | 16 +++++++++++- profiles/tests/test_gql_my_profile_query.py | 27 +++++++++++++++++++++ profiles/tests/test_utils.py | 12 ++++++++- profiles/utils.py | 14 +++++++++++ 4 files changed, 67 insertions(+), 2 deletions(-) diff --git a/profiles/schema.py b/profiles/schema.py index e1cd8851..52e6167f 100644 --- a/profiles/schema.py +++ b/profiles/schema.py @@ -1,3 +1,4 @@ +import logging from itertools import chain import django.dispatch @@ -72,11 +73,14 @@ VerifiedPersonalInformationTemporaryAddress, ) from .utils import ( + enum_values, force_list, requester_can_view_verified_personal_information, requester_has_sufficient_loa_to_perform_gdpr_request, ) +logger = logging.getLogger(__name__) + User = get_user_model() AllowedEmailType = graphene.Enum.from_enum( @@ -617,7 +621,17 @@ def resolve_login_methods(self: Profile, info, **kwargs): # logic here. # Can remove this after Tunnistamo is no longer in use. Related ticket: HP-2495 if amr.intersection({"helsinki_tunnus", "heltunnistussuomifi", "suomi_fi"}): - return get_user_login_methods(self.user.uuid) + login_methods = get_user_login_methods(self.user.uuid) + login_methods_in_enum = { + val for val in login_methods if val in enum_values(LoginMethodType) + } + if unknown_login_methods := login_methods - login_methods_in_enum: + logger.warning( + "Found login methods which are not part of the LoginMethodType enum: %s", + unknown_login_methods, + ) + + return login_methods_in_enum return [] diff --git a/profiles/tests/test_gql_my_profile_query.py b/profiles/tests/test_gql_my_profile_query.py index 1e01e7b9..3a6218f2 100644 --- a/profiles/tests/test_gql_my_profile_query.py +++ b/profiles/tests/test_gql_my_profile_query.py @@ -637,3 +637,30 @@ def mock_return(*_, **__): ) assert "errors" not in executed assert executed["data"]["myProfile"]["loginMethods"] == [] + + +def test_user_does_not_see_non_enum_login_methods( + user_gql_client, profile, group, service, monkeypatch +): + def mock_return(*_, **__): + return {"password", "this should not show up"} + + monkeypatch.setattr( + "profiles.keycloak_integration.get_user_identity_providers", mock_return + ) + + profile = ProfileFactory(user=user_gql_client.user) + ServiceConnectionFactory(profile=profile, service=service) + + query = """ + { + myProfile { + loginMethods + } + } + """ + executed = user_gql_client.execute( + query, auth_token_payload={"amr": "helsinki_tunnus"}, service=service + ) + assert "errors" not in executed + assert set(executed["data"]["myProfile"]["loginMethods"]) == {"PASSWORD"} diff --git a/profiles/tests/test_utils.py b/profiles/tests/test_utils.py index 96d69146..56e28231 100644 --- a/profiles/tests/test_utils.py +++ b/profiles/tests/test_utils.py @@ -1,6 +1,8 @@ +from enum import Enum + import pytest -from profiles.utils import force_list +from profiles.utils import enum_values, force_list @pytest.mark.parametrize( @@ -15,3 +17,11 @@ ) def test_force_list(input_value, expected): assert force_list(input_value) == expected + + +def test_enum_values(): + class TestEnum(Enum): + FOO = "foo" + BAR = "bar" + + assert enum_values(TestEnum) == ["foo", "bar"] diff --git a/profiles/utils.py b/profiles/utils.py index 8f6fe6be..813ce547 100644 --- a/profiles/utils.py +++ b/profiles/utils.py @@ -1,5 +1,11 @@ +import functools +from enum import Enum +from typing import Type, TypeVar + from django.conf import settings +_EnumType = TypeVar("_EnumType", bound=Type[Enum]) + def requester_has_service_permission(request, permission): service = getattr(request, "service", None) @@ -52,3 +58,11 @@ def force_list(value) -> list: if isinstance(value, list): return value return [value] + + +@functools.cache +def enum_values(enum: _EnumType) -> list: + """ + Return a list of values from the given enum. + """ + return [e.value for e in enum]