Skip to content

Commit 1645f79

Browse files
authored
Merge pull request #1526 from maykinmedia/task/2932-eherkenning-login-choice
[#2932] Skip KVK branch selection if vestigingsnummer already selected
2 parents 89451c6 + fcb4c6b commit 1645f79

File tree

3 files changed

+113
-2
lines changed

3 files changed

+113
-2
lines changed

src/eherkenning/backends.py

+12
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
from digid_eherkenning.exceptions import eHerkenningError
55
from digid_eherkenning.utils import get_client_ip
66

7+
from open_inwoner.kvk.branches import KVK_BRANCH_SESSION_VARIABLE
8+
79
UserModel = get_user_model()
810

911

@@ -12,6 +14,12 @@ class eHerkenningBackend(_eHerkenningBackend):
1214
Custom backend to identify users based on the KvK number instead of RSIN
1315
"""
1416

17+
def get_company_branch_number(self, attributes):
18+
company_branch_number = attributes.get(
19+
"urn:etoegang:1.9:ServiceRestriction:Vestigingsnr", None
20+
)
21+
return company_branch_number
22+
1523
def get_or_create_user(self, request, saml_response, saml_attributes):
1624
kvk = self.get_kvk_number(saml_attributes)
1725
if kvk == "":
@@ -26,6 +34,10 @@ def get_or_create_user(self, request, saml_response, saml_attributes):
2634
user = UserModel.eherkenning_objects.eherkenning_create(kvk)
2735
created = True
2836

37+
if vestigingsnummer := self.get_company_branch_number(saml_attributes):
38+
self.request.session[KVK_BRANCH_SESSION_VARIABLE] = vestigingsnummer
39+
self.request.session.save()
40+
2941
success_message = self.error_messages["login_success"] % {
3042
"user": str(user),
3143
"user_info": " (new account)" if created else "",

src/open_inwoner/accounts/backends.py

+38-2
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,20 @@
55
from django.contrib.auth import get_user_model
66
from django.contrib.auth.backends import ModelBackend
77
from django.contrib.auth.hashers import check_password
8+
from django.contrib.auth.models import AbstractUser
89
from django.urls import reverse, reverse_lazy
910

1011
from axes.backends import AxesBackend
1112
from digid_eherkenning.oidc.backends import BaseBackend
1213
from mozilla_django_oidc_db.backends import OIDCAuthenticationBackend
1314
from mozilla_django_oidc_db.config import dynamic_setting
15+
from mozilla_django_oidc_db.typing import JSONObject
1416
from oath import accept_totp
1517

1618
from open_inwoner.configurations.models import SiteConfiguration
19+
from open_inwoner.kvk.branches import KVK_BRANCH_SESSION_VARIABLE
1720
from open_inwoner.utils.hash import generate_email_from_string
21+
from open_inwoner.utils.views import LogMixin
1822

1923
from .choices import LoginTypeChoices
2024
from .models import OpenIDDigiDConfig, OpenIDEHerkenningConfig
@@ -147,7 +151,7 @@ def filter_users_by_claims(self, claims):
147151
return self.UserModel.objects.filter(**{"oidc_id__iexact": unique_id})
148152

149153

150-
class DigiDEHerkenningOIDCBackend(BaseBackend):
154+
class DigiDEHerkenningOIDCBackend(LogMixin, BaseBackend):
151155
OIP_UNIQUE_ID_USER_FIELDNAME = dynamic_setting[Literal["bsn", "kvk"]]()
152156
OIP_LOGIN_TYPE = dynamic_setting[LoginTypeChoices]()
153157

@@ -158,6 +162,26 @@ def _check_candidate_backend(self) -> bool:
158162
OpenIDEHerkenningConfig,
159163
)
160164

165+
def _store_vestigingsnummer_in_session(self, claims: JSONObject):
166+
"""Get company vestigingsnummer from OIDC claims & store in session"""
167+
168+
eherkenning_config = self.config_class.get_solo()
169+
170+
branch_number_claim = eherkenning_config.branch_number_claim[0]
171+
if not (vestigingsnummer := claims.get(branch_number_claim)):
172+
return
173+
174+
self.request.session[KVK_BRANCH_SESSION_VARIABLE] = vestigingsnummer
175+
self.request.session.save()
176+
177+
identifier_claim = eherkenning_config.identifier_type_claim[0]
178+
kvk_or_rsin = claims.get(identifier_claim)
179+
180+
self.log_system_action(
181+
f"Vestigingsnummer {vestigingsnummer} retrieved from IdP for "
182+
f"eHerkenning user (KVK/RSIN: {kvk_or_rsin})"
183+
)
184+
161185
def filter_users_by_claims(self, claims):
162186
"""Return all users matching the specified subject."""
163187
unique_id = self._extract_username(claims)
@@ -169,7 +193,11 @@ def filter_users_by_claims(self, claims):
169193
)
170194

171195
def create_user(self, claims):
172-
"""Return object for a newly created user account."""
196+
"""
197+
Return object for a newly created user account.
198+
199+
Get vestigingsnummer from OIDC claims & store in session
200+
"""
173201

174202
unique_id = self._extract_username(claims)
175203

@@ -185,4 +213,12 @@ def create_user(self, claims):
185213
}
186214
)
187215

216+
if self.config_class is OpenIDEHerkenningConfig:
217+
self._store_vestigingsnummer_in_session(claims)
218+
188219
return user
220+
221+
def update_user(self, user: AbstractUser, claims: JSONObject):
222+
if self.config_class is OpenIDEHerkenningConfig:
223+
self._store_vestigingsnummer_in_session(claims)
224+
return super().update_user(user, claims)

src/open_inwoner/accounts/tests/test_oidc_views.py

+63
Original file line numberDiff line numberDiff line change
@@ -1920,3 +1920,66 @@ def test_redirect_after_login_no_registration_and_no_branch_selection(
19201920
profile_response = self.app.get(profile_response.url)
19211921

19221922
self.assertEqual(profile_response.status_code, 200)
1923+
1924+
@patch("open_inwoner.kvk.client.KvKClient.get_all_company_branches")
1925+
@patch("open_inwoner.utils.context_processors.SiteConfiguration")
1926+
@patch("mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.get_userinfo")
1927+
@patch("mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.store_tokens")
1928+
@patch("mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.verify_token")
1929+
@patch("mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.get_token")
1930+
@patch(
1931+
"open_inwoner.accounts.models.OpenIDEHerkenningConfig.get_solo",
1932+
return_value=OpenIDEHerkenningConfig(
1933+
id=1,
1934+
enabled=True,
1935+
legal_subject_claim=["kvk"],
1936+
oidc_op_authorization_endpoint="http://idp.local/auth",
1937+
),
1938+
)
1939+
def test_redirect_after_login_branch_already_selected(
1940+
self,
1941+
mock_get_solo,
1942+
mock_get_token,
1943+
mock_verify_token,
1944+
mock_store_tokens,
1945+
mock_get_userinfo,
1946+
mock_siteconfig,
1947+
mock_kvk,
1948+
):
1949+
"""
1950+
KVK branch selection should be skipped if KVK_BRANCH_SESSION_VARIABLE is present in session
1951+
"""
1952+
user = eHerkenningUserFactory.create(kvk="12345678", rsin="123456789")
1953+
mock_get_userinfo.return_value = {
1954+
"sub": "some_username",
1955+
"kvk": "12345678",
1956+
"urn:etoegang:1.9:ServiceRestriction:Vestigingsnr": "123456789000",
1957+
}
1958+
mock_siteconfig.return_value = SiteConfiguration(id=1, eherkenning_enabled=True)
1959+
mock_kvk.return_value = [
1960+
{"kvkNummer": "12345678"},
1961+
{"kvkNummer": "87654321"},
1962+
]
1963+
1964+
self.assertEqual(User.objects.count(), 1)
1965+
1966+
redirect_url = reverse("profile:detail")
1967+
1968+
callback_response = perform_oidc_login(
1969+
self.app, "eherkenning", redirect_url=redirect_url
1970+
)
1971+
1972+
user = User.objects.get()
1973+
1974+
self.assertEqual(user.pk, int(self.app.session.get("_auth_user_id")))
1975+
self.assertEqual(user.kvk, "12345678")
1976+
self.assertEqual(
1977+
self.app.session.get(KVK_BRANCH_SESSION_VARIABLE), "123456789000"
1978+
)
1979+
1980+
self.assertRedirects(
1981+
callback_response, reverse("profile:detail"), fetch_redirect_response=False
1982+
)
1983+
1984+
response = self.app.get(callback_response.url)
1985+
self.assertEqual(response.status_code, 200)

0 commit comments

Comments
 (0)