Skip to content

Commit

Permalink
Merge pull request #716 from SUNET/ylle_reference_nin
Browse files Browse the repository at this point in the history
Enable updating nin
  • Loading branch information
johanlundberg authored Nov 28, 2024
2 parents c35040e + 89fea9e commit 4dfafcc
Show file tree
Hide file tree
Showing 21 changed files with 432 additions and 77 deletions.
2 changes: 2 additions & 0 deletions src/eduid/userdb/proofing/user.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from eduid.userdb import User
from eduid.userdb.identity import IdentityType

__author__ = "lundberg"


class ProofingUser(User):
replace_locked: IdentityType | None = None
pass
18 changes: 14 additions & 4 deletions src/eduid/webapp/bankid/tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -583,9 +583,13 @@ def test_mfa_token_verify_wrong_verified_nin(self) -> None:

self._verify_user_parameters(eppn, identity=nin, identity_present=False)

@patch("eduid.webapp.common.api.helpers.get_reference_nin_from_navet_data")
@patch("eduid.common.rpc.am_relay.AmRelay.request_user_sync")
def test_mfa_token_verify_no_verified_nin(self, mock_request_user_sync: MagicMock) -> None:
def test_mfa_token_verify_no_verified_nin(
self, mock_request_user_sync: MagicMock, mock_reference_nin: MagicMock
) -> None:
mock_request_user_sync.side_effect = self.request_user_sync
mock_reference_nin.return_value = None

eppn = self.test_unverified_user_eppn
nin = self.test_user_nin
Expand Down Expand Up @@ -739,9 +743,11 @@ def test_webauthn_token_verify_backdoor(self, mock_request_user_sync: MagicMock)

self._verify_user_parameters(eppn, identity=nin, identity_verified=True, token_verified=True, num_proofings=2)

@patch("eduid.webapp.common.api.helpers.get_reference_nin_from_navet_data")
@patch("eduid.common.rpc.am_relay.AmRelay.request_user_sync")
def test_nin_verify(self, mock_request_user_sync: MagicMock) -> None:
def test_nin_verify(self, mock_request_user_sync: MagicMock, mock_reference_nin: MagicMock) -> None:
mock_request_user_sync.side_effect = self.request_user_sync
mock_reference_nin.return_value = None

eppn = self.test_unverified_user_eppn
self._verify_user_parameters(eppn, num_mfa_tokens=0, identity_verified=False)
Expand Down Expand Up @@ -770,9 +776,11 @@ def test_nin_verify(self, mock_request_user_sync: MagicMock) -> None:
assert doc["given_name"] == "Ûlla"
assert doc["surname"] == "Älm"

@patch("eduid.webapp.common.api.helpers.get_reference_nin_from_navet_data")
@patch("eduid.common.rpc.am_relay.AmRelay.request_user_sync")
def test_nin_verify_signup_auth(self, mock_request_user_sync: MagicMock) -> None:
def test_nin_verify_signup_auth(self, mock_request_user_sync: MagicMock, mock_reference_nin: MagicMock) -> None:
mock_request_user_sync.side_effect = self.request_user_sync
mock_reference_nin.return_value = None

eppn = self.test_unverified_user_eppn
self._verify_user_parameters(eppn, num_mfa_tokens=0, identity_verified=False)
Expand Down Expand Up @@ -836,9 +844,11 @@ def test_mfa_login_no_nin(self) -> None:

self._verify_user_parameters(eppn, num_mfa_tokens=0, identity_verified=False, num_proofings=0)

@patch("eduid.webapp.common.api.helpers.get_reference_nin_from_navet_data")
@patch("eduid.common.rpc.am_relay.AmRelay.request_user_sync")
def test_mfa_login_unverified_nin(self, mock_request_user_sync: MagicMock) -> None:
def test_mfa_login_unverified_nin(self, mock_request_user_sync: MagicMock, mock_reference_nin: MagicMock) -> None:
mock_request_user_sync.side_effect = self.request_user_sync
mock_reference_nin.return_value = None
eppn = self.test_unverified_user_eppn

# Add locked nin to user
Expand Down
15 changes: 14 additions & 1 deletion src/eduid/webapp/common/api/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@
FluxResponseStatus,
FluxSuccessResponse,
)
from eduid.webapp.common.api.utils import get_user
from eduid.webapp.common.api.utils import get_reference_nin_from_navet_data, get_user
from eduid.webapp.common.session import session
from eduid.webapp.letter_proofing.app import LetterProofingApp
from eduid.webapp.lookup_mobile_proofing.app import MobileProofingApp
from eduid.webapp.oidc_proofing.app import OIDCProofingApp

__author__ = "lundberg"

Expand Down Expand Up @@ -116,6 +119,16 @@ def verify_identity_decorator(*args: Any, **kwargs: Any) -> EduidViewReturnType:
if isinstance(locked_nin, NinIdentity) and locked_nin.number != kwargs["nin"]:
logger.info("User has a different locked NIN")
logger.debug(f"Locked NIN: {locked_nin.number}. New NIN: {kwargs['nin']}")
if isinstance(session.app, MobileProofingApp | LetterProofingApp | OIDCProofingApp):
ref = get_reference_nin_from_navet_data(kwargs["nin"])
logger.debug(f"New NIN has reference NIN: {ref}")
# If the reference NIN is the same as the locked NIN, we can continue with the verification
if locked_nin.number == ref:
logger.info(
"User has a different locked NIN but it is the same as the reference NIN for the new NIN"
)
return f(*args, **kwargs)

return error_response(message=CommonMsg.user_has_other_locked_nin)

return f(*args, **kwargs)
Expand Down
26 changes: 20 additions & 6 deletions src/eduid/webapp/common/api/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from eduid.userdb.user import TUserSubclass, User
from eduid.userdb.userdb import UserDB
from eduid.webapp.common.api.app import EduIDBaseApp
from eduid.webapp.common.api.utils import get_from_current_app, save_and_sync_user
from eduid.webapp.common.api.utils import get_from_current_app, get_reference_nin_from_navet_data, save_and_sync_user

__author__ = "lundberg"

Expand Down Expand Up @@ -235,17 +235,23 @@ def verify_nin_for_user(
current_app.logger.debug(f"NIN: {proofing_user.identities.nin.number}")
return True

reference_nin = get_reference_nin_from_navet_data(proofing_state.nin.number)
if reference_nin is not None:
current_app.logger.debug(f"verified nin has reference_nin: {reference_nin}")

if (
proofing_user.locked_identity.nin is not None
and proofing_user.locked_identity.nin.number != proofing_state.nin.number
and proofing_user.locked_identity.nin.number != reference_nin
):
raise LockedIdentityViolation("users locked nin does not match verified nin or reference nin")

# check if the users current nin is the same as the one just verified
# if there is no locked nin identity or the locked nin identity matches we can replace the current nin identity
# with the one just verified
if proofing_user.identities.nin.number != proofing_state.nin.number:
current_app.logger.info("users current nin does not match the nin just verified")
current_app.logger.debug(f"{proofing_user.identities.nin.number} != {proofing_state.nin.number}")
if (
proofing_user.locked_identity.nin is not None
and proofing_user.locked_identity.nin.number != proofing_state.nin.number
):
raise LockedIdentityViolation("users locked nin does not match verified nin")

# user has no locked nin identity or the user has previously verified the nin
# replace the never verified nin with the one just verified
Expand All @@ -268,6 +274,14 @@ def verify_nin_for_user(
proofing_user.identities.nin.proofing_method = proofing_method
proofing_user.identities.nin.proofing_version = proofing_log_entry.proofing_version

# Replace locked nin with verified nin if it has changed in the population registry
if (
reference_nin is not None
and proofing_user.locked_identity.nin is not None
and proofing_user.locked_identity.nin.number == reference_nin
):
proofing_user.replace_locked = IdentityType.NIN

# Update users name
proofing_user = set_user_names_from_nin_proofing(user=proofing_user, proofing_log_entry=proofing_log_entry)

Expand Down
11 changes: 11 additions & 0 deletions src/eduid/webapp/common/api/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from eduid.userdb.db import BaseDB
from eduid.userdb.element import ElementKey
from eduid.userdb.fixtures.users import UserFixtures
from eduid.userdb.identity import IdentityType
from eduid.userdb.logs.db import ProofingLog
from eduid.userdb.proofing.state import NinProofingState
from eduid.userdb.testing import MongoTemporaryInstance, SetupConfig
Expand Down Expand Up @@ -271,10 +272,15 @@ def request_user_sync(self, private_user: User, app_name_override: str | None =

central_user = self.app.central_userdb.get_user_by_id(private_user.user_id)
private_user_dict = private_user.to_dict()
replace_locked: IdentityType | None = None
# fix signup_user data
if "proofing_reference" in private_user_dict:
del private_user_dict["proofing_reference"]

if "replace_locked" in private_user_dict:
replace_locked = private_user_dict["replace_locked"]
del private_user_dict["replace_locked"]

if central_user is None:
# This is a new user, create a new user in the central db
self.app.central_userdb.save(User.from_dict(private_user_dict))
Expand Down Expand Up @@ -303,6 +309,11 @@ def request_user_sync(self, private_user: User, app_name_override: str | None =
identity.created_by = "test"
user.locked_identity.add(identity)
continue
if replace_locked is locked_identity.identity_type:
# replace the locked identity with the new verified identity
if identity.created_by is None:
identity.created_by = "test"
user.locked_identity.replace(identity)

# Restore metadata that is necessary for the consistency checks in the save() function
user.modified_ts = central_user.modified_ts
Expand Down
Loading

0 comments on commit 4dfafcc

Please sign in to comment.