Skip to content

Commit

Permalink
Make minimum age check more flexible
Browse files Browse the repository at this point in the history
  • Loading branch information
KiOui committed Sep 19, 2023
1 parent 582cc2f commit a259483
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 22 deletions.
16 changes: 15 additions & 1 deletion website/age/admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
from django.contrib import admin
from django.contrib.admin import register

# Register your models here.
from age.models import AgeRegistration


@register(AgeRegistration)
class AgeRegistrationAdmin(admin.ModelAdmin):
"""Admin interface for the age registration model."""

list_display = ("user", "name", "minimum_age", "created_at")
search_fields = ("user__username",)
ordering = ("created_at",)

def name(self, instance):
"""Name of the user."""
return instance.user.__str__()
6 changes: 4 additions & 2 deletions website/age/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 4.2.4 on 2023-09-18 20:45
# Generated by Django 4.2.4 on 2023-09-19 07:27

from django.conf import settings
from django.db import migrations, models
Expand All @@ -15,10 +15,12 @@ class Migration(migrations.Migration):

operations = [
migrations.CreateModel(
name="Is18YearsOld",
name="AgeRegistration",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("minimum_age", models.PositiveIntegerField()),
(
"user",
models.OneToOneField(
Expand Down
12 changes: 8 additions & 4 deletions website/age/models.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import uuid

from django.contrib.auth import get_user_model
from django.db import models


User = get_user_model()


class Is18YearsOld(models.Model):
"""Class to check whether someone has a legal drinking age."""
class AgeRegistration(models.Model):
"""Class to save an age registration."""

user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="is_18_years_old")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
minimum_age = models.PositiveIntegerField()

def __str__(self):
"""Convert this object to string."""
return f"{self.user} ({self.created_at})"
53 changes: 53 additions & 0 deletions website/age/services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import json

from django.conf import settings
from django.contrib.auth import get_user_model

from age import models

User = get_user_model()


def verify_minimum_age(user: User, minimum_age: int = 18) -> bool:
"""Verify whether someone has a certain minimum age."""
try:
age_registration = models.AgeRegistration.objects.get(user=user)
except models.AgeRegistration.DoesNotExist:
return False

return age_registration.minimum_age >= minimum_age


def construct_disclose_tree():
"""Construct the disclose tree."""
yivi_disclose_tree = []
yivi_age_disclose_tree = []
for yivi_key in settings.AGE_VERIFICATION_MINIMUM_AGE_MAPPING.keys():
yivi_age_disclose_tree.append([yivi_key])

yivi_disclose_tree.append(yivi_age_disclose_tree)
if settings.AGE_VERIFICATION_USERNAME_ATTRIBUTE is not None:
yivi_disclose_tree.append([[settings.AGE_VERIFICATION_USERNAME_ATTRIBUTE]])
return yivi_disclose_tree


def get_proven_attributes_from_proof_tree(proof_tree):
"""Get the proven attributes from a proof tree."""
proven_attributes = []
for attribute_conjuction_clause in proof_tree:
for possibly_proven_attribute in attribute_conjuction_clause:
if possibly_proven_attribute["status"] == "PRESENT":
attribute_id = possibly_proven_attribute["id"]
proven_attributes.append(attribute_id)
return proven_attributes


def get_highest_proven_age_from_proven_attributes(proven_attributes: [str]):
"""Get the highest minimum age from the proven attributes."""
highest_age = None
for proven_attribute in proven_attributes:
if proven_attribute in settings.AGE_VERIFICATION_MINIMUM_AGE_MAPPING.keys():
proven_attribute_minimum_age = settings.AGE_VERIFICATION_MINIMUM_AGE_MAPPING[proven_attribute]
if highest_age is None or highest_age < proven_attribute_minimum_age:
highest_age = proven_attribute_minimum_age
return highest_age
29 changes: 18 additions & 11 deletions website/age/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,30 @@
from django.dispatch import receiver

from age import models
from age.services import get_proven_attributes_from_proof_tree, get_highest_proven_age_from_proven_attributes
from yivi.models import Session
from yivi.signals import attributes_verified


@receiver(attributes_verified)
def update_is_over_18(sender, **kwargs):
def update_minimum_age_when_proven(sender, **kwargs):
"""Update the minimum age of someone when proven by Yivi."""
session: Session = kwargs.get("session")
if session.user is None or models.Is18YearsOld.objects.filter(user=session.user).exists():
if session.user is None:
return

attributes = kwargs.get("attributes")
# TODO: How to verify proven attributes with requested attributes?
for attribute_conjuction_clause in attributes:
for attribute_disjunction_clause in attribute_conjuction_clause:
attribute_id = attribute_disjunction_clause["id"]
if (
attribute_id == settings.AGE_VERIFICATION_DISCLOSE_ATTRIBUTE
and attribute_disjunction_clause["status"] == "PRESENT"
):
models.Is18YearsOld.objects.create(user=session.user)
proven_attributes = get_proven_attributes_from_proof_tree(attributes)
minimum_proven_age = get_highest_proven_age_from_proven_attributes(proven_attributes)
if minimum_proven_age is None:
return

try:
age_registration = models.AgeRegistration.objects.get(user=session.user)
except models.AgeRegistration.DoesNotExist:
models.AgeRegistration.objects.create(user=session.user, minimum_age=minimum_proven_age)
return

if age_registration.minimum_age < minimum_proven_age:
age_registration.minimum_age = minimum_proven_age
age_registration.save()
5 changes: 3 additions & 2 deletions website/age/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from django.views.generic import TemplateView

from age import models
from age.services import verify_minimum_age, construct_disclose_tree


class AgeOverviewView(LoginRequiredMixin, TemplateView):
Expand All @@ -18,12 +19,12 @@ class AgeOverviewView(LoginRequiredMixin, TemplateView):
def get(self, request, **kwargs):
"""Get Age Overview View."""

is_18_years_old = models.Is18YearsOld.objects.filter(user=request.user).exists()
is_18_years_old = verify_minimum_age(request.user)
rendered_tab = render_to_string(
"age/age_overview.html",
context={
"is_over_18": is_18_years_old,
"disclose": mark_safe(json.dumps({"disclose": [[[settings.AGE_VERIFICATION_DISCLOSE_ATTRIBUTE]]]})),
"disclose": mark_safe(json.dumps({"disclose": construct_disclose_tree()})),
},
)

Expand Down
12 changes: 10 additions & 2 deletions website/tosti/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,5 +263,13 @@

DJANGO_CRON_DELETE_LOGS_OLDER_THAN = 14

# TODO: We might rather place [[["irma-demo.MijnOverheid.ageLower.over18"]]] here and create a function that can verify the requested attribute combination below with the proven attributes in age/signals.py
AGE_VERIFICATION_DISCLOSE_ATTRIBUTE = "irma-demo.MijnOverheid.ageLower.over18"
AGE_VERIFICATION_MINIMUM_AGE_MAPPING = {
"pbdf.gemeente.personalData.over18": 18,
"pbdf.pbdf.ageLimits.over18": 18,
"pbdf.pilot-amsterdam.passport.over18": 18,
"pbdf.bzkpilot.personalData.over18": 18,
"pbdf.pilot-amsterdam.idcard.over18": 18,
"pbdf.nijmegen.ageLimits.over18": 18,
}
# TODO: Add verification username attribute
AGE_VERIFICATION_USERNAME_ATTRIBUTE = None

0 comments on commit a259483

Please sign in to comment.