-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[#3607] Refactor validation plugin, add BRK validator
- Loading branch information
Showing
15 changed files
with
279 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
from django.contrib import admin | ||
|
||
from solo.admin import SingletonModelAdmin | ||
|
||
from .models import BRKConfig | ||
|
||
|
||
@admin.register(BRKConfig) | ||
class BRKConfigAdmin(SingletonModelAdmin): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
from django.apps import AppConfig | ||
from django.utils.translation import gettext_lazy as _ | ||
|
||
|
||
class BRKApp(AppConfig): | ||
name = "openforms.contrib.brk" | ||
label = "brk" | ||
verbose_name = _("BRK configuration") | ||
|
||
def ready(self): | ||
# register the plugin | ||
from . import validators # noqa |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import logging | ||
from typing import TypedDict | ||
|
||
import requests | ||
|
||
from openforms.contrib.hal_client import HALClient | ||
from zgw_consumers_ext.api_client import ServiceClientFactory | ||
|
||
from .models import BRKConfig | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class NoServiceConfigured(RuntimeError): | ||
pass | ||
|
||
|
||
def get_client() -> "BRKClient": | ||
config = BRKConfig.get_solo() | ||
assert isinstance(config, BRKConfig) | ||
if not (service := config.service): | ||
raise NoServiceConfigured("No KVK service configured!") | ||
service_client_factory = ServiceClientFactory(service) | ||
return BRKClient.configure_from(service_client_factory) | ||
|
||
|
||
class SearchParams(TypedDict, total=False): | ||
postcode: str | ||
huisnummer: str | ||
huisletter: str | ||
huisnummertoevoeging: str | ||
|
||
|
||
class BRKClient(HALClient): | ||
def get_cadastrals_by_address(self, query_params: SearchParams): | ||
""" | ||
Search for cadastrals by querying for a specifc address. | ||
API docs: https://vng-realisatie.github.io/Haal-Centraal-BRK-bevragen/swagger-ui-1.5#/Kadastraal%20Onroerende%20Zaken/GetKadastraalOnroerendeZaken | ||
""" | ||
assert query_params, "You must provide at least one query parameter" | ||
|
||
try: | ||
response = self.get( | ||
"kadastraalonroerendezaken", | ||
params=query_params, | ||
) | ||
response.raise_for_status() | ||
except requests.RequestException as exc: | ||
logger.exception("exception while making BRK request", exc_info=exc) | ||
raise exc | ||
|
||
return response.json() | ||
|
||
def get_cadastral_titleholders_by_cadastral_id(self, cadastral_id: str): | ||
""" | ||
Search for commercial titleholders of a cadastral immovable property. | ||
API docs: https://vng-realisatie.github.io/Haal-Centraal-BRK-bevragen/swagger-ui-1.5#/Zakelijke%20Gerechtigden/GetZakelijkGerechtigden | ||
""" | ||
try: | ||
response = self.get( | ||
f"kadastraalonroerendezaken/{cadastral_id}/zakelijkgerechtigden", | ||
) | ||
response.raise_for_status() | ||
except requests.RequestException as exc: | ||
logger.exception("exception while making BRK request", exc_info=exc) | ||
raise exc | ||
|
||
return response.json() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# Generated by Django 3.2.23 on 2023-11-29 16:05 | ||
|
||
from django.db import migrations, models | ||
import django.db.models.deletion | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
initial = True | ||
|
||
dependencies = [ | ||
("zgw_consumers", "0019_alter_service_uuid"), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name="BRKConfig", | ||
fields=[ | ||
( | ||
"id", | ||
models.AutoField( | ||
auto_created=True, | ||
primary_key=True, | ||
serialize=False, | ||
verbose_name="ID", | ||
), | ||
), | ||
( | ||
"service", | ||
models.OneToOneField( | ||
help_text="Service for API interaction with the BRK.", | ||
limit_choices_to={"api_type": "orc"}, | ||
null=True, | ||
on_delete=django.db.models.deletion.PROTECT, | ||
related_name="+", | ||
to="zgw_consumers.service", | ||
verbose_name="BRK API", | ||
), | ||
), | ||
], | ||
options={ | ||
"verbose_name": "BRK configuration", | ||
}, | ||
), | ||
] |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
from django.db import models | ||
from django.utils.translation import gettext_lazy as _ | ||
|
||
from solo.models import SingletonModel | ||
from zgw_consumers.constants import APITypes | ||
|
||
|
||
class BRKConfig(SingletonModel): | ||
""" | ||
Global configuration and defaults. | ||
""" | ||
|
||
service = models.OneToOneField( | ||
"zgw_consumers.Service", | ||
verbose_name=_("BRK API"), | ||
help_text=_("Service for API interaction with the BRK."), | ||
on_delete=models.PROTECT, | ||
limit_choices_to={"api_type": APITypes.orc}, | ||
related_name="+", | ||
null=True, | ||
) | ||
|
||
class Meta: | ||
verbose_name = _("BRK configuration") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
from typing import TypedDict | ||
|
||
from django.contrib.auth.hashers import check_password as check_salted_hash | ||
from django.core.exceptions import ValidationError | ||
from django.utils.deconstruct import deconstructible | ||
from django.utils.translation import gettext_lazy as _ | ||
|
||
from requests import RequestException | ||
|
||
from openforms.authentication.constants import AuthAttribute | ||
from openforms.submissions.models import Submission | ||
from openforms.validations.registry import register | ||
|
||
from .client import NoServiceConfigured, SearchParams, get_client | ||
|
||
|
||
class AddressValue(TypedDict, total=False): | ||
postcode: str | ||
housenumber: str | ||
houseletter: str | ||
housenumberaddition: str | ||
|
||
|
||
@register( | ||
"brk-Zaakgerechtigde", | ||
verbose_name=_("BRK - Zaakgerechtigde"), | ||
for_components=("address",), | ||
) | ||
@deconstructible | ||
class BRKZaakgerechtigdeValidator: | ||
|
||
error_messages = { | ||
"not_found": _("%(type)s does not exist."), | ||
} | ||
|
||
def __call__(self, value: AddressValue, submission: Submission) -> bool: | ||
|
||
try: | ||
client = get_client() | ||
except NoServiceConfigured: | ||
raise ValidationError( | ||
self.error_messages["not_found"], | ||
params={"type": _("Owner")}, | ||
) | ||
|
||
address_query: SearchParams = { | ||
"postcode": value["postcode"], | ||
"huisnummer": value["housenumber"], | ||
"huisletter": value["houseletter"], | ||
"huisnummertoevoeging": value["housenumberaddition"], | ||
} | ||
|
||
# We assume submission has auth_info available, should we verify that this validator is used | ||
# on a step that requires login? | ||
if submission.auth_info.attribute != AuthAttribute.bsn: | ||
raise ValidationError() | ||
|
||
try: | ||
with client: | ||
cadastrals_resp = client.get_cadastrals_by_address(address_query) | ||
kadastraals = cadastrals_resp["_embedded"]["kadastraalOnroerendeZaken"] | ||
if len(kadastraals) > 1: | ||
# The query by address returned more than one cadastral, this shouldn't happen | ||
raise ValidationError() | ||
kadastraal_id = kadastraals[0]["identificatie"] | ||
|
||
titleholders_resp = client.get_cadastral_titleholders_by_cadastral_id( | ||
kadastraal_id | ||
) | ||
|
||
bsns = [ | ||
a["persoon"]["identificatie"] | ||
for a in titleholders_resp["_embedded"]["zakelijkGerechtigden"] | ||
] | ||
if submission.auth_info.attribute_hashed: | ||
is_valid = any( | ||
check_salted_hash(bsn, submission.auth_info.value) | ||
for bsn in bsns | ||
) | ||
else: | ||
is_valid = submission.auth_info.value in bsns | ||
|
||
if not is_valid: | ||
raise ValidationError() | ||
|
||
except (RequestException, KeyError): | ||
# TODO raise not found? | ||
pass | ||
|
||
return True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters