feat: link local library

This commit is contained in:
gabdevele
2026-06-17 20:43:56 +02:00 Unverified
parent 9a770a2af0
commit db59882f33
327 changed files with 22274 additions and 1 deletions
@@ -0,0 +1,47 @@
from django.contrib import admin
from django.contrib import messages
from django.db import IntegrityError
from .models import OnBoardingRegistration
from spid_cie_oidc.authority.models import FederationDescendant, FederationDescendantContact
@admin.register(OnBoardingRegistration)
class OnBoardingRegistrationAdmin(admin.ModelAdmin):
@admin.action(description='enable descendant')
def enable_as_descendant(modeladmin, request, queryset):
for entity_onboarded in queryset:
name = entity_onboarded.organization_name
sub = entity_onboarded.url_entity
jwks = entity_onboarded.public_jwks
_type = entity_onboarded.type
contact = entity_onboarded.contact
try:
entity = FederationDescendant.objects.create(
name = name,
sub = sub,
type = _type,
jwks = jwks,
is_active = True,
status = "valid"
)
FederationDescendantContact.objects.create(
entity = entity,
contact = contact,
type = "email"
)
entity_onboarded.status = "onboarded"
entity_onboarded.save()
except IntegrityError: # pragma: no cover
messages.error(request, f"Already exists a descendant with subject: {sub}")
list_display = (
"organization_name",
"url_entity",
"type",
"authn_buttons_page_url",
"public_jwks",
)
actions = [enable_as_descendant]
@@ -0,0 +1,8 @@
from django.apps import AppConfig
class SpidCieOidcOnboardingConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "spid_cie_oidc.onboarding"
verbose_name = "SPID/CIE OIDC Federation OnBoarding"
label = "spid_cie_oidc_onboarding"
@@ -0,0 +1,134 @@
import logging
from django import forms
from django.utils.translation import gettext_lazy as _
from spid_cie_oidc.entity.validators import validate_public_jwks
from spid_cie_oidc.authority.validators import validate_entity_configuration
from .validators import unique_entity_url
from spid_cie_oidc.entity.models import ENTITY_TYPES
from django.conf import settings
logger = logging.getLogger(__name__)
try:
OIDCFED_TRUST_ANCHORS = getattr(
settings, "OIDCFED_TRUST_ANCHORS"
)
except AttributeError: # pragma: no cover
OIDCFED_TRUST_ANCHORS = []
logger.warning(
"OIDCFED_TRUST_ANCHORS not configured in your settings file."
)
class OnboardingRegistrationForm(forms.Form):
organization_name = forms.CharField(
initial="",
label=_("Organization Name"),
error_messages={"required": _("Enter your organization name")},
)
url_entity = forms.URLField(
initial="",
label=_("Entity URL"),
error_messages={"required": _("Enter the entity unique identifier (HTTP URL)")},
validators=[validate_entity_configuration, unique_entity_url],
)
authn_buttons_page_url = forms.URLField(
initial="",
label=_("URL of the login page, if available"),
error_messages={
"required": _(
"Enter the url of the page where the SPID/CIE button is available"
)
},
required=False,
)
auth_request_url = forms.CharField(
initial="",
label=_("Authentication request URL, if available"),
required = False
)
contact = forms.EmailField(
initial="",
label=_("Contact email"),
error_messages={"required": _("Enter your contact email")},
)
type = forms.ChoiceField(
choices=[(i, i) for i in ENTITY_TYPES],
label="",
error_messages={"required": _("Select a entity type")},
)
public_jwks = forms.JSONField(
initial=list,
label=_("Public Federation JWKs"),
error_messages={
"required": _(
"Enter the public JWKs published in your Entity Configuration, "
"as double check."
)
},
validators=[validate_public_jwks],
)
class OnboardingCreateTrustChain(forms.Form):
sub = forms.URLField(
initial="",
label=_("url of subject"),
error_messages={"required": _("Enter the subject")},
)
type = forms.ChoiceField(
choices=[(i, i) for i in ENTITY_TYPES],
label="OpenID Connect Federation entity type",
error_messages={"required": _("Select a entity type")},
)
trust_anchor = forms.ChoiceField(
choices=[(i, i) for i in OIDCFED_TRUST_ANCHORS],
label=_("url of the trust anchor"),
error_messages={"required": _("Enter the url of trust anchor")},
)
class OnboardingValidatingTrustMarkForm(forms.Form):
sub = forms.URLField(
initial="",
label=_("Url of subject"),
required=False
)
id = forms.URLField(
initial="",
label=_("Id"),
required=False
)
trust_mark = forms.CharField(
initial = "",
label=_("Enter trust mark"),
required=False
)
class OnboardingDecodeForm(forms.Form):
jwt = forms.CharField(
initial="",
label=_("jwt"),
error_messages={"required": _("Enter a jwt")},
)
jwk = forms.JSONField(
initial=dict,
label=_("jwk"),
required=False,
)
@@ -0,0 +1,85 @@
# Generated by Django 4.0.2 on 2022-02-22 08:39
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import spid_cie_oidc.entity.validators
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name="OnBoardingRegistration",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"organization_name",
models.CharField(help_text="Organization Name. ", max_length=254),
),
(
"url_entity",
models.URLField(
help_text="URL of the Entity.", max_length=254, unique=True
),
),
(
"authn_buttons_page_url",
models.URLField(
help_text="URL of the page where the SPID/CIE button is available.",
max_length=254,
),
),
(
"public_jwks",
models.JSONField(
default=dict,
help_text="Public jwks of the Entities",
validators=[
spid_cie_oidc.entity.validators.validate_public_jwks
],
),
),
(
"status",
models.CharField(
choices=[
("onboarded", "onboarded"),
("failed", "failed"),
("processing", "processing"),
("caquired", "acquired"),
],
default="acquired",
max_length=33,
),
),
(
"created_by",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.PROTECT,
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"verbose_name": "OnBoarding Registration",
"verbose_name_plural": "OnBoarding Registrations",
},
),
]
@@ -0,0 +1,35 @@
# Generated by Django 4.0.2 on 2022-02-23 12:23
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
("spid_cie_oidc_onboarding", "0001_initial"),
]
operations = [
migrations.AlterModelOptions(
name="onboardingregistration",
options={
"ordering": ["created"],
"verbose_name": "OnBoarding Registration",
"verbose_name_plural": "OnBoarding Registrations",
},
),
migrations.AddField(
model_name="onboardingregistration",
name="created",
field=models.DateTimeField(
auto_now_add=True, default=django.utils.timezone.now
),
preserve_default=False,
),
migrations.AddField(
model_name="onboardingregistration",
name="modified",
field=models.DateTimeField(auto_now=True),
),
]
@@ -0,0 +1,24 @@
# Generated by Django 4.0.2 on 2022-02-25 16:04
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
(
"spid_cie_oidc_onboarding",
"0002_alter_onboardingregistration_options_and_more",
),
]
operations = [
migrations.AlterModelOptions(
name="onboardingregistration",
options={
"ordering": ["-created"],
"verbose_name": "OnBoarding Registration",
"verbose_name_plural": "OnBoarding Registrations",
},
),
]
@@ -0,0 +1,19 @@
# Generated by Django 4.0.2 on 2022-03-11 13:16
from django.db import migrations, models
import spid_cie_oidc.entity.validators
class Migration(migrations.Migration):
dependencies = [
('spid_cie_oidc_onboarding', '0003_alter_onboardingregistration_options'),
]
operations = [
migrations.AlterField(
model_name='onboardingregistration',
name='public_jwks',
field=models.JSONField(default=list, help_text='Public jwks of the Entities', validators=[spid_cie_oidc.entity.validators.validate_public_jwks]),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.0.2 on 2022-03-16 16:08
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('spid_cie_oidc_onboarding', '0004_alter_onboardingregistration_public_jwks'),
]
operations = [
migrations.AlterField(
model_name='onboardingregistration',
name='status',
field=models.CharField(choices=[('onboarded', 'onboarded'), ('failed', 'failed'), ('processing', 'processing'), ('acquired', 'acquired')], default='acquired', max_length=33),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.0.2 on 2022-03-18 17:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('spid_cie_oidc_onboarding', '0005_alter_onboardingregistration_status'),
]
operations = [
migrations.AddField(
model_name='onboardingregistration',
name='type',
field=models.CharField(blank=True, choices=[('federation_entity', 'federation_entity'), ('openid_relying_party', 'openid_relying_party'), ('openid_provider', 'openid_provider'), ('oauth_resource', 'oauth_resource')], default='openid_relying_party', help_text='OpenID Connect Federation entity type', max_length=33),
),
]
@@ -0,0 +1,20 @@
# Generated by Django 4.0.2 on 2022-03-22 16:50
import datetime
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('spid_cie_oidc_onboarding', '0006_onboardingregistration_type'),
]
operations = [
migrations.AddField(
model_name='onboardingregistration',
name='contact',
field=models.CharField(default=datetime.date(2022, 3, 22), help_text='any kind of contact type, usually an email.', max_length=255),
preserve_default=False,
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.0.3 on 2022-03-28 13:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('spid_cie_oidc_onboarding', '0007_onboardingregistration_contact'),
]
operations = [
migrations.AddField(
model_name='onboardingregistration',
name='auth_request_url',
field=models.CharField(blank=True, help_text='SPID/CIE authentication request trigger url.', max_length=254, null=True),
),
]
@@ -0,0 +1,30 @@
# Generated by Django 4.2.1 on 2023-07-09 15:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("spid_cie_oidc_onboarding", "0008_onboardingregistration_auth_request_url"),
]
operations = [
migrations.AlterField(
model_name="onboardingregistration",
name="type",
field=models.CharField(
blank=True,
choices=[
("federation_entity", "federation_entity"),
("openid_relying_party", "openid_relying_party"),
("openid_provider", "openid_provider"),
("oauth_resource", "oauth_resource"),
("wallet_provider", "wallet_provider"),
("wallet_relying_party", "wallet_relying_party"),
],
default="openid_relying_party",
help_text="OpenID Connect Federation entity type",
max_length=33,
),
),
]
@@ -0,0 +1,31 @@
# Generated by Django 4.2.7 on 2023-11-14 14:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("spid_cie_oidc_onboarding", "0009_alter_onboardingregistration_type"),
]
operations = [
migrations.AlterField(
model_name="onboardingregistration",
name="type",
field=models.CharField(
blank=True,
choices=[
("federation_entity", "federation_entity"),
("openid_relying_party", "openid_relying_party"),
("openid_provider", "openid_provider"),
("openid_credential_issuer", "openid_credential_issuer"),
("oauth_resource", "oauth_resource"),
("wallet_provider", "wallet_provider"),
("wallet_relying_party", "wallet_relying_party"),
],
default="openid_relying_party",
help_text="OpenID Connect Federation entity type",
max_length=33,
),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.0.5 on 2026-03-23 15:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('spid_cie_oidc_onboarding', '0010_alter_onboardingregistration_type'),
]
operations = [
migrations.AlterField(
model_name='onboardingregistration',
name='type',
field=models.CharField(blank=True, choices=[('federation_entity', 'federation_entity'), ('openid_relying_party', 'openid_relying_party'), ('openid_provider', 'openid_provider'), ('openid_credential_issuer', 'openid_credential_issuer'), ('oauth_resource', 'oauth_resource'), ('wallet_provider', 'wallet_provider'), ('openid_credential_verifier', 'openid_credential_verifier')], default='openid_relying_party', help_text='OpenID Connect Federation entity type', max_length=33),
),
]
@@ -0,0 +1,81 @@
from django.db import models
from django.contrib.auth import get_user_model
from django.utils.translation import gettext as _
from spid_cie_oidc.entity.abstract_models import TimeStampedModel
from spid_cie_oidc.entity.validators import validate_public_jwks
from spid_cie_oidc.entity.models import ENTITY_TYPES
class OnBoardingRegistration(TimeStampedModel):
organization_name = models.CharField(
max_length=254, help_text=_("Organization Name. ")
)
url_entity = models.URLField(
max_length=254,
blank=False,
null=False,
unique=True,
help_text=_("URL of the Entity."),
)
authn_buttons_page_url = models.URLField(
max_length=254,
blank=False,
null=False,
help_text=_("URL of the page where the SPID/CIE button is available."),
)
auth_request_url = models.CharField(
max_length=254,
blank=True,
null= True,
help_text=_("SPID/CIE authentication request trigger url."),
)
public_jwks = models.JSONField(
blank=False,
help_text=_("Public jwks of the Entities"),
default=list,
validators=[validate_public_jwks],
)
type = models.CharField(
max_length=33,
blank=True,
default="openid_relying_party",
choices=[(i, i) for i in ENTITY_TYPES],
help_text=_("OpenID Connect Federation entity type"),
)
contact = models.CharField(
max_length=255,
blank=False,
null=False,
help_text=_("any kind of contact type, usually an email."),
)
status = models.CharField(
max_length=33,
choices=(
("onboarded", "onboarded"),
("failed", "failed"),
("processing", "processing"),
("acquired", "acquired"),
),
default="acquired",
)
created_by = models.ForeignKey(
get_user_model(), blank=True, null=True, on_delete=models.PROTECT
)
class Meta:
verbose_name = "OnBoarding Registration"
verbose_name_plural = "OnBoarding Registrations"
ordering = ["-created"]
def __str__(self):
return f"{self.organization_name} {self.url_entity}"
Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="500px" height="500px" viewBox="0 0 500 500" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>logo-it-2021</title>
<g id="logo-it-2021" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Group">
<rect id="Rectangle" fill="#0066CC" x="0" y="0" width="500" height="500"/>
<path d="M362.336089,118.811881 L362.336089,174.651845 L410.541772,174.651845 L410.541772,220.827199 L361.986773,220.827199 L361.986773,303.513298 C361.986773,311.746114 362.336089,317.115341 362.685406,320.694825 C363.034723,323.916362 364.43199,326.77995 366.877205,329.28559 C369.322421,331.791228 373.164903,332.865075 378.40465,332.865075 L408.445874,332.149178 L410.891089,375.102995 C393.425261,379.040428 379.801917,381.188119 370.719686,381.188119 C346.966162,381.188119 330.897601,375.818892 322.514004,364.722489 C313.781091,353.984035 309.589292,333.93892 309.589292,304.587145 L309.589292,118.811881 L362.336089,118.811881 Z M255.958929,178.442844 L255.958929,375.225023 L202.328566,375.225023 L202.328566,178.442844 L255.958929,178.442844 Z M121.883022,166.516652 C131.351098,166.516652 139.362548,169.707713 145.553213,176.089838 C151.743878,182.471963 154.657132,190.272338 154.657132,199.845526 C154.657132,209.418712 151.743878,216.864525 145.553213,223.24665 C139.726703,228.919649 132.079412,232.110711 122.247179,232.110711 C112.779102,232.110711 104.767652,228.919649 98.5769872,222.537524 C92.386322,216.155399 89.1089109,208.355025 89.1089109,199.1364 C89.1089109,189.917776 92.386322,182.117401 98.2128302,175.735276 C104.403495,169.353151 112.414945,166.516652 121.883022,166.516652 Z" id="Combined-Shape-Copy-2" fill="#FFFFFF"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

@@ -0,0 +1,202 @@
<!-- Extends default Bootstrap Unical Italia template -->
{% extends 'bootstrap-italia-base.html' %}
{% load static %}
{% load i18n %}
{% block extra_head %}
{{ block.super }}
{% endblock extra_head %}
{% block page_title %}
{% trans "OIDC Federation onboarding system" %}
{% endblock page_title %}
<!-- URL link top left -->
{% block header_slim_org_url %}
https://www.spid.gov.it/
{% endblock header_slim_org_url %}
<!-- Name top left -->
{% block header_slim_org_name %}
{% trans "OIDC Federation onboarding system" %}
{% endblock header_slim_org_name %}
<!-- Mobile slim_org_name -->
{% block header_slim_mobile_org_name %}
{% trans "OIDC Federation onboarding system" %}
{% endblock header_slim_mobile_org_name %}
<!-- Make empty areas -->
{% block header_mobile_arrow %}{% endblock header_mobile_arrow %}
{% block header_mobile_slim_menu %}{% endblock header_mobile_slim_menu %}
<!-- Logo in Header -->
{% block header_center_logo %}
{% endblock header_center_logo %}
<!-- Organization name in Header -->
{% block header_center_org_name %}
{% trans "OIDC Federation onboarding system" %}
{% endblock header_center_org_name %}
<!-- Organization description in Header -->
{% block header_center_org_subname %}
{% trans "OIDC Federation onboarding system" %}
{% endblock header_center_org_subname %}
{% block menu_links %}
<li class="nav-item ">
<a class="nav-link {% if request.resolver_match.view_name == 'oidc_onboarding_landing' %}active{% endif %}"
href="{% url 'oidc_onboarding_landing' %}">
<span>{% trans 'Home' %}</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link {% if request.resolver_match.view_name == 'oidc_onboarding_entities' %}active{% endif %}"
href="{% url 'oidc_onboarding_entities' %}">
<span>{% trans 'Entities onboarded' %}</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link {% if request.resolver_match.view_name == 'oidc_onboarding_registration' %}active{% endif %}"
href="{% url 'oidc_onboarding_registration' %}">
<span>{% trans 'Register your entity' %}</span>
</a>
</li>
<li class="nav-item dropdown megamenu">
<a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-expanded="true">
<span>Tools</span>
<svg class="icon icon-m">
<use xlink:href="/static/svg/sprite.svg#it-expand"></use>
</svg>
</a>
<div class="dropdown-menu">
<div class="row">
<div class="col-12 col-lg-4">
<div class="link-list-wrapper">
<ul class="link-list">
<li>
<h3 class="no_toc">JWK</h3>
</li>
<li><a class="list-item" href="{% url 'oidc_onboarding_create_jwk'%}">
<span>{% trans "Create a jwk" %}</span></a></li>
<li><a class="list-item" href="{% url 'oidc_onboarding_convert_jwk'%}?type=private">
<span>{% trans "Convert a private JWK to PEM" %}</span></a></li>
<li><a class="list-item" href="{% url 'oidc_onboarding_convert_jwk'%}?type=public">
<span>{% trans "Convert a public JWK to PEM" %}</span></a></li>
<li><a class="list-item" href="{% url 'oidc_onboarding_convert_pem'%}?type=private">
<span>{% trans "Convert a private PEM to JWK" %}</span></a></li>
<li><a class="list-item" href="{% url 'oidc_onboarding_convert_pem'%}?type=public">
<span>{% trans "Convert a public PEM to JWK" %}</span></a></li>
<li><a class="list-item" href="{% url 'oidc_onboarding_tools_decode_jwt' %}">
<span>{% trans "JWT decode and verification" %}</span></a></li>
<li>
<h3 class="no_toc">Utils</h3>
</li>
<li><a class="list-item" href="{% url 'oidc_onboarding_resolve_statement' %}">
<span>{% trans "Resolve entity statement" %}</span></a></li>
<li><a class="list-item" href="{% url 'oidc_onboarding_tools_apply_policy' %}">
<span>{% trans "Apply policy" %}</span></a></li>
</ul>
</div>
</div>
<div class="col-12 col-lg-4">
<div class="link-list-wrapper">
<ul class="link-list">
<li>
<h3 class="no_toc">VALIDATORS</h3>
</li>
<li><a class="list-item" href="{% url 'oidc_onboarding_validate_md' %}?metadata_type=op_metadata&provider_profile=spid">
<span>{% trans "Validate OP metadata spid" %}</span></a></li>
<li><a class="list-item" href="{% url 'oidc_onboarding_validate_md' %}?metadata_type=op_metadata&provider_profile=cie">
<span>{% trans "Validate OP metadata cie" %}</span></a></li>
<li><a class="list-item" href="{% url 'oidc_onboarding_validate_md' %}?metadata_type=rp_metadata&provider_profile=spid">
<span>{% trans "Validate RP metadata spid" %}</span></a></li>
<li><a class="list-item" href="{% url 'oidc_onboarding_validate_md' %}?metadata_type=rp_metadata&provider_profile=cie">
<span>{% trans "Validate RP metadata cie" %}</span></a></li>
<li><a class="list-item" href="{% url 'oidc_onboarding_validate_authn_request_jwt'%}?provider_profile=spid">
<span>{% trans "Validate Authn Request spid" %}</span></a></li>
<li><a class="list-item" href="{% url 'oidc_onboarding_validate_authn_request_jwt'%}?provider_profile=cie">
<span>{% trans "Validate Authn Request cie" %}</span></a></li>
<li><a class="list-item" href="{% url 'oidc_onboarding_validate_ec' %}">
<span>{% trans "Validate Entity Configuration" %}</span></a></li>
<li><a class="list-item" href="{% url 'oidc_onboarding_validating_trustmark' %}">
<span>{% trans "Trust mark validation" %}</span></a></li>
</ul>
</div>
</div>
<div class="col-12 col-lg-4">
<div class="link-list-wrapper">
<ul class="link-list">
<li>
<h3 class="no_toc">{% trans "Schemas" %}</h3>
</li>
<li><a class="list-item" href="{% url 'oidc_onboarding_schemas_federation_entity' %}">
<span>Federation Entity Endpoint</span></a></li>
<li><a class="list-item" href="{% url 'oidc_onboarding_schemas_Authorization' %}">
<span>Authorization Endpoint</span></a></li>
<li><a class="list-item" href="{% url 'oidc_onboarding_schemas_introspection' %}">
<span>Introspection Endpoint</span></a></li>
<li><a class="list-item" href="{% url 'oidc_onboarding_schemas_metadata' %}">
<span>Metadata</span></a></li>
<li><a class="list-item" href="{% url 'oidc_onboarding_schemas_token' %}">
<span>Token Endpoint</span></a></li>
<li><a class="list-item" href="{% url 'oidc_onboarding_schemas_revocation' %}">
<span>Revocation Endpoint</span></a></li>
<li><a class="list-item" href="{% url 'oidc_onboarding_schemas_jwt_client_assertion' %}">
<span>Jwt client Assertion</span></a></li>
</ul>
</div>
</div>
</div>
</div>
</li>
{% endblock menu_links %}
{% block container %}
<div>
<div class="container pt-2 p-3">
<div class="row d-lg-flex">
<div class="col-12">
<div class="card-wrapper card-space">
<div class="card card-bg no-after">
<div class="card-body pl-lg-0">
<div class="row h-100">
{% block centered_container %}{% endblock %}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock container %}
<!-- Logo in Footer -->
{% block footer_logo %}
{% endblock footer_logo %}
<!-- Organization name in Footer -->
{% block footer_org_name %}
{% trans "OIDC Federation onboarding system" %}
{% endblock footer_org_name %}
<!-- Organization name in Footer -->
{% block footer_org_subname %}
{% trans "OIDC Federation onboarding system" %}
{% endblock footer_org_subname %}
{% block extra_scripts %}
{% block bootstrap_italia_validation %}
{% endblock bootstrap_italia_validation %}
{% endblock extra_scripts %}
{% block footer_menu_section %}
{% endblock footer_menu_section %}
@@ -0,0 +1,65 @@
<!-- Extends default Bootstrap Unical Italia template -->
{% extends 'base.html' %}
{% load i18n %}
{% block centered_container %}
<div class="col-12 pl-lg-4">
<h4 class="text-left">
{% trans "Apply policy" %}
</h4>
<p class="card-title">
{% trans "Adheres a metadata policy to a metadata statement" %}
</p>
<form method="get" action="">
{% csrf_token %}
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
<div class="form-row">
<div class="col-12 form-group my-3">
<label for="id_md">
<span class="form-label-content">
metadata statement
</span>
</label>
<textarea id="id_md" rows="9"
name="md">{{ md }}</textarea>
</div>
</div>
<div class="form-row">
<div class="col-12 form-group my-3">
<label for="id_policy">
<span class="form-label-content">
metadata policy
</span>
</label>
<textarea id="id_policy" rows="5"
name="policy">{{ policy }}</textarea>
</div>
</div>
</div>
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
<div class="form-row">
<div class="col-12 form-group my-3">
<label for="id_result">
<span class="form-label-content">
result
</span>
</label>
<textarea id="id_result" rows="16" disabled
name="result">{{ result }}</textarea>
</div>
</div>
</div>
</div>
<input type="submit" name="decode" class="btn btn-lg btn-primary btn-block"
value="{% trans 'apply' %}" />
</form>
</div>
{% endblock %}
@@ -0,0 +1,47 @@
<!-- Extends default Bootstrap Unical Italia template -->
{% extends 'base.html' %}
{% load i18n %}
{% block centered_container %}
<div class="col-12 pl-lg-4">
<h4 class="text-left">
{% trans "Convert jwk to pem" %}
</h4>
<p class="card-title">
{% trans "Enter a jwk in the field below to encode it in PEM format." %}
</p>
<form method="post" action="">
{% csrf_token %}
<div class="form-row">
<div class="col-12 form-group my-3">
<label for="id_jwk">
<span class="form-label-content">
Jwk
</span>
</label>
<textarea id="id_jwk" rows="12" name="jwk">{{ jwk }}</textarea>
</div>
</div>
{% if pem %}
<h4 class="text-left">
{% if jwk_type == 'private' %}
{% trans "Private key" %}
{% else %}
{% trans "Public key" %}
{% endif %}
</h4>
<div class="form-row">
<div class="col-12 form-group my-3">
<textarea id="private_pem" rows="13" name="private_pem">{{ pem }}</textarea>
</div>
</div>
{% endif %}
<input type="submit" name="confirm" class="btn btn-lg btn-success btn-block"
value="{% trans 'Convert to pem' %}" />
</form>
</div>
{% endblock %}
@@ -0,0 +1,51 @@
<!-- Extends default Bootstrap Unical Italia template -->
{% extends 'base.html' %}
{% load i18n %}
{% block centered_container %}
<div class="col-12 pl-lg-4">
<h4 class="text-left">
{% if pem_type == 'private' %}
{% trans "Convert private pem to jwk" %}
{% else %}
{% trans "Convert Public pem to jwk" %}
{% endif %}
</h4>
<p class="card-title">
{% trans "Enter a pem in the field below to decode it in jwk format." %}
</p>
<form method="post" action="">
{% csrf_token %}
<div class="form-row">
<div class="col-12 form-group my-3">
<label for="id_pem">
<span class="form-label-content">
Pem
</span>
</label>
<textarea id="id_pem" rows="12" name="pem">{{ pem }}</textarea>
</div>
</div>
{% if jwk %}
<h4 class="text-left">
{% if pem_type == 'private' %}
{% trans "Private pem" %}
{% else %}
{% trans "Public pem" %}
{% endif %}
</h4>
<div class="form-row">
<div class="col-12 form-group my-3">
<textarea id="jwk" rows="13" name="jwk">{{ jwk }}</textarea>
</div>
</div>
{% endif %}
<input type="submit" name="confirm" class="btn btn-lg btn-success btn-block"
value="{% trans 'Convert to jwk' %}" />
</form>
</div>
{% endblock %}
@@ -0,0 +1,75 @@
<!-- Extends default Bootstrap Unical Italia template -->
{% extends 'base.html' %}
{% load i18n %}
{% block centered_container %}
<div class="col-12 pl-lg-4">
<h4 class="text-left">
{% trans "JWT decode and verification" %}
</h4>
<p class="card-title">
{% trans "Submit a jwt to decode it, if you submit also a jwk you'll decrypt or verify it." %}
</p>
<form method="post" action="">
{% csrf_token %}
<div class="row">
<div class="col-12 col-lg-7">
{% csrf_token %}
{% for form_field in form %}
<div class="form-row">
{% if form_field.name == 'jwt' %}
<div class="col-12 form-group my-3">
<label for="id_{{ form_field.html_name }}">
<span class="form-label-content">
{{ form_field.label }}
</span>
</label>
<textarea id="id_{{ form_field.html_name }}" rows="14"
name="{{ form_field.name }}">{{ form.jwt.value}}</textarea>
</div>
{% else %}
<div class="col-12 form-group my-3">
<label for="id_{{ form_field.html_name }}">
<span class="form-label-content mt-4">
{{ form_field.label }}
</span>
</label>
<textarea id="id_{{ form_field.html_name }}" rows="6"
name="{{ form_field.name }}">{{ form.jwk.value }}</textarea>
</div>
{% endif %}
<span for="id_{{ form_field.html_name }}" class="text-danger">{{ form_field.errors }}</span>
</div>
{% endfor %}
</div>
<div class="col-12 col-lg-5">
<div class="form-row">
<div class="col-12 form-group my-3">
<label for="id_head">
<span class="form-label-content">
Head
</span>
</label>
<textarea id="id_head" rows="6" name="head">{{ head }}</textarea>
</div>
</div>
<div class="form-row">
<div class="col-12 form-group my-2">
<label for="id_payload">
Payload
</label>
<textarea id="id_payload" rows="12" name="payload">{{ payload }}</textarea>
</div>
</div>
<input type="submit" name="decode" class="btn btn-lg btn-success btn-block"
value="{% trans 'decode or verify' %}" />
</div>
</div>
</form>
</div>
{% endblock %}
@@ -0,0 +1,80 @@
<!-- Extends default Bootstrap Unical Italia template -->
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% block centered_container %}
<div class="col-12 pl-lg-4">
<h4 class="text-left">
{% trans "Entities onboarded"%}
</h4>
<table class="table table-striped">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">{% trans "organization name" %}</th>
<th scope="col">{% trans "url entity" %}</th>
<th scope="col">{% trans "status" %}</th>
</tr>
</thead>
<tbody>
{% for entity in entities %}
<tr>
<th scope="row">{{ forloop.counter }}</th>
<td>{{ entity.organization_name }}</td>
<td>{{ entity.url_entity }}</td>
{% if entity.status == 'processing' %}
<td class="complementary-2-color-b1">{{ entity.status }}</td>
{% elif entity.status == 'failed' %}
<td class="text-danger">{{ entity.status }}</td>
{% elif entity.status == 'onboarded' %}
<td class="analogue-2-color-b2">{{ entity.status }}</td>
{% else %}
<td class="text-secondary">{{ entity.status }}</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
<nav class="pagination-wrapper justify-content-center">
<ul class="pagination">
{% if entities.has_previous %}
<li class="page-item">
<a class="page-link" href="?page={{entities.previous_page_number}}" tabindex="-1" aria-hidden="true">
<svg class="icon icon-primary">
<use href="{% static 'svg/sprite.svg' %}#it-chevron-left"></use>
</svg>
<span class="sr-only">{% trans "previous page" %}</span>
</a>
</li>
{% endif %}
{% for i in entities.paginator.page_range%}
{% if i == entities.number %}
<li class="page-item active">
<a class="page-link" href="?page={{ i }}" aria-current="page">
<span class="d-inline-block d-sm-none">{% trans "page" %}</span>{{ i }}
</a>
</li>
{% else %}
<li class="page-item"><a class="page-link" href="?page={{ i }}">{{ i }}</a></li>
{% endif %}
{% endfor %}
{% if entities.has_next%}
<li class="page-item">
<a class="page-link" href="?page={{entities.next_page_number}}">
<span class="sr-only">{% trans "next page" %}</span>
<svg class="icon icon-primary">
<use href="{% static 'svg/sprite.svg' %}#it-chevron-right"></use>
</svg>
</a>
</li>
{% endif %}
</ul>
</nav>
</div>
{% endblock %}
@@ -0,0 +1,31 @@
<!-- Extends default Bootstrap Unical Italia template -->
{% extends 'base.html' %}
{% load i18n %}
{% block centered_container %}
<div class="col-12 pl-lg-4">
<h4 class="text-left">
{% trans "Create a JWK" %}
</h4>
<p class="pt-4 mb-0">
{% trans "In this page you have a brand new JWK. Refresh the page to gent another one." %}
</p>
<div class="form-row">
<div class="col-12 form-group my-3">
<textarea id="jwk" rows="14" name="jwk">{{ private_jwk }}</textarea>
</div>
</div>
<p class="pt-4 mb-0">
{% trans "Here the public version of the previous private JWK." %}
</p>
<div class="form-row">
<div class="col-12 form-group my-3">
<textarea id="jwk" rows="14" name="jwk">{{ public_jwk }}</textarea>
</div>
</div>
</div>
{% endblock %}
@@ -0,0 +1,82 @@
<!-- Extends default Bootstrap Unical Italia template -->
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% block container %}
<div>
<div class="container pt-2 p-3">
<div class="d-lg-flex">
<div class="col-12">
<div class="row">
<div class="card-wrapper card-space col-6">
<div class="card card-bg no-after">
<div class="card-body pl-lg-0">
<div class="h-100">
<div class="col-12">
<div class="img-responsive-wrapper d-none d-lg-block h-100">
<div class="img-responsive img-responsive-panoramic h-100 ml-4 mt-2">
<figure class="img-wrapper">
<img alt="superman" class="h-100" src="{% static 'images/SPID-e-CIE-696x383.jpg' %}" title="spid-cie">
</figure>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card-wrapper card-space col-6">
<div class="card card-bg no-after">
<div class="card-body pl-lg-0">
<div class="row h-100">
<div class="col-12 pl-lg-4">
<div class="row m-2">
<h4 class="text-left">
{% trans "Welcome" %}
</h4>
<p class="card-title">
{% blocktrans %}
The SPID/CIE onboarding demostration system wants to exemplify what
information is required to
register and test a leaf entity within the OIDC Federation.
{% endblocktrans %}
</p>
<p class="card-title">
{% blocktrans %}
In this demonstration consider that you are registering your RP or OP to a Federation Authority.
{% endblocktrans %}
</p>
</div>
<div class="row mt-3">
<div class="col">
<span class="badge" style="font-size: 100%">
<a href="{% url 'oidc_onboarding_entities' %}" class="btn btn-secondary btn-block">
{% trans "Show accredited entities" %}
</a>
</span>
<span class="badge mb-3 mr-2 ml-0 pr-10 p-2 mw-100" style="font-size: 100%">
<a href="{% url 'oidc_onboarding_registration' %}" class="btn btn-primary btn-block">
{% trans "Register your entity" %}
</a>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock container %}
@@ -0,0 +1,59 @@
<!-- Extends default Bootstrap Unical Italia template -->
{% extends 'base.html' %}
{% load i18n %}
{% block centered_container %}
<div class="col-12 pl-lg-4">
<h4 class="text-left">
{% trans "Onboard your entity" %}
</h4>
<p class="card-title">
{% blocktrans %}
Welcome to the example onboarding system of spid-cie-oidc-django.
In this page you can register a OIDC Federation entity to be onboarded in the federation.
The form has some preliminar validations for your entity configuration,
once submitted, validated and tested, a Federation operator will enable your entity as a Descendant of the Federation Authority.
{% endblocktrans%}
</p>
<form method="post" action="">
{% csrf_token %}
{% for form_field in form %}
<div class="form-row">
<div class="col-12 form-group my-3">
<label for="id_{{ form_field.html_name }}">
<span class="form-label-content">
{{ form_field.label }}
</span>
</label>
{% if form_field.field.widget.input_type == 'select'%}
<div class="bootstrap-select-wrapper mb-4">
<select name="{{ form_field.name }}" id="id_{{ form_field.html_name }}">
{% for value, label in form_field.field.choices %}
<option value="{{ value }}" {% if form_field.value == value %} selected {% endif %}>
{{ label }}
</option>
{% endfor %}
</select>
</div>
{% elif form_field.name == 'public_jwks' %}
<textarea id="id_{{ form_field.html_name }}" rows="5"
name="{{ form_field.name }}">{{ form.public_jwks.value}}</textarea>
{% else %}
<input type="{{ form_field.field.widget.input_type }}" class="form-control"
id="id_{{ form_field.html_name }}" name="{{ form_field.name }}" value="{{form_field.value}}">
{% endif %}
</div>
<span for="id_{{ form_field.html_name }}_help" class="text-info">{{ form_field.help_text }}</span>
<span for="id_{{ form_field.html_name }}" class="text-danger">{{ form_field.errors }}</span>
</div>
{% endfor %}
<input type="submit" name="confirm" class="btn btn-lg btn-success btn-block" value="{% trans 'Submit' %}" />
</form>
</div>
{% endblock %}
@@ -0,0 +1,59 @@
<!-- Extends default Bootstrap Unical Italia template -->
{% extends 'base.html' %}
{% load i18n %}
{% block centered_container %}
<div class="col-12 pl-lg-4">
<h4 class="text-left">
{% trans "Resolve Entity Statement" %}
</h4>
<p class="card-title">
{% trans "By inserting the sub of an entity and the trust anchor you can resolve the statement of the entity" %}
</p>
<form method="get" action="">
{% csrf_token %}
{% for form_field in form %}
{% if form_field.field.widget.input_type == 'select'%}
<div class="bootstrap-select-wrapper my-4">
<select name="{{ form_field.name }}" id="id_{{ form_field.html_name }}">
{% for value, label in form_field.field.choices %}
<option value="{{ value }}" {% if form_field.value == value %} selected {% endif %}>
{{ label }}
</option>
{% endfor %}
</select>
<span for="id_{{ form_field.html_name }}" class="text-danger">{{ form_field.errors }}</span>
</div>
{% else %}
<div class="form-row">
<div class="col-12 form-group my-2">
<label for="id_{{ form_field.html_name }}">
<span class="form-label-content">
{{ form_field.label }}
</span>
</label>
<input type="{{ form_field.field.widget.input_type }}" class="form-control"
id="id_{{ form_field.html_name }}" name="{{ form_field.name }}" value="{{form_field.value}}">
</div>
<span for="id_{{ form_field.html_name }}" class="text-danger">{{ form_field.errors }}</span>
</div>
{% endif %}
{% endfor %}
<div class="form-row">
<div class="col-12 form-group">
<textarea id="resolved_statement" rows="13" name="resolved_statement"
disabled>{{ resolved_statement }}</textarea>
</div>
</div>
<input type="submit" name="confirm" class="btn btn-lg btn-success btn-block" value="{% trans 'resolve' %}" />
</form>
</div>
{% endblock %}
@@ -0,0 +1,34 @@
<!-- Extends default Bootstrap Unical Italia template -->
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% block centered_container %}
<div class="col-12 col-lg-12">
<div class="ml-4 mb-4">
<h4 class="text-left">
{% trans title %}
</h4>
</div>
<div class="pl-lg-3">
<div id="collapseDiv-sc" class="collapse-div collapse-background-active" role="tablist">
{% for k, v in schemas.items %}
<div class="collapse-header" id="heading1-sc{{ forloop.counter }}">
<button data-toggle="collapse" data-target="#collapse{{ forloop.counter }}-sc{{ forloop.counter }}"
aria-expanded="false" aria-controls="collapse{{ forloop.counter }}-sc{{ forloop.counter }}">
{% trans k %}
</button>
</div>
<div id="collapse{{ forloop.counter }}-sc{{ forloop.counter }}" class="collapse"
role="tabpanel" aria-labelledby="heading{{ forloop.counter }}-sc{{ forloop.counter }}">
<div class="collapse-body">
<pre>{{ v | linebreaks}}</pre>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endblock %}
@@ -0,0 +1,45 @@
<!-- Extends default Bootstrap Unical Italia template -->
{% extends 'base.html' %}
{% load i18n %}
{% block centered_container %}
<div class="col-12 pl-lg-4">
<h4 class="text-left">
{% trans "Validate entity configuration" %}
</h4>
<p class="pt-4 mb-4">
{% trans "enter a url of entity to check if it is compatible" %}
</p>
<form method="post" action="">
{% csrf_token %}
<div class="form-row">
<div class="col-12 form-group my-3">
<label for="id_url">
<span class="form-label-content">
{% trans "url of the entity" %}
</span>
</label>
<input type="url" class="form-control" id="id_url" name="url" value="{{url}}">
</div>
<span for="id_url" class="text-danger"></span>
</div>
{% if ec %}
<div class="form-row mt-3">
<div class="col-12 form-group my-3">
<label for="id_ec">
<span class="form-label-content">
{% trans "Here the json of entity configuration." %}
</span>
</label>
<textarea id="id_ec" rows="14" disabled>{{ ec }}</textarea>
</div>
</div>
{% endif %}
<input type="submit" name="confirm" class="btn btn-lg btn-primary btn-block" value="{% trans 'validate' %}" />
</form>
</div>
{% endblock %}
@@ -0,0 +1,33 @@
<!-- Extends default Bootstrap Unical Italia template -->
{% extends 'base.html' %}
{% load i18n %}
{% block centered_container %}
<div class="col-12 pl-lg-4">
<h4 class="text-left">
{% blocktrans %}
{{title}}
{% endblocktrans %}
</h4>
<p class="pt-4 mb-2">
{% trans description %}
</p>
<form method="post" action="">
{% csrf_token %}
<div class="form-row">
<div class="col-12 form-group my-3">
<label for="id_md">
<span class="form-label-content">
{{field_name}}
</span>
</label>
<textarea id="id_md" rows="14" name="md">{{ md }}</textarea>
</div>
</div>
<input type="submit" name="confirm" class="btn btn-lg btn-success btn-block"
value="{% trans 'validate' %}" />
</form>
</div>
{% endblock %}
@@ -0,0 +1,41 @@
<!-- Extends default Bootstrap Unical Italia template -->
{% extends 'base.html' %}
{% load i18n %}
{% block centered_container %}
<div class="col-12 pl-lg-4">
<h4 class="text-left">
{% trans 'Validating Trust Mark' %}
</h4>
<p class="card-title">
{% trans "To validate a trust mark insert the entity's sub and the id or directly the trust mark" %}
</p>
<form method="get" action="">
{% csrf_token %}
{% for form_field in form %}
<div class="form-row">
<div class="col-12 form-group my-3">
<label for="id_{{ form_field.html_name }}">
<span class="form-label-content">
{{ form_field.label }}
</span>
</label>
{% if form_field.name == 'trust_mark'%}
<textarea id="id_{{ form_field.html_name }}" rows="4"
name="{{ form_field.name }}">{{ form.trust_mark.value}}</textarea>
{% else%}
<input type="{{ form_field.field.widget.input_type }}" class="form-control"
id="id_{{ form_field.html_name }}" name="{{ form_field.name }}" value="{{form_field.value}}">
{% endif %}
</div>
<span for="id_{{ form_field.html_name }}" class="text-danger">{{ form_field.errors }}</span>
</div>
{% endfor %}
<input type="submit" name="confirm" class="btn btn-lg btn-success btn-block" value="{% trans 'validate' %}" />
</div>
{% endblock %}
@@ -0,0 +1,17 @@
from django.test import Client
import logging
logger = logging.getLogger(__name__)
class OnboardingRegistrationResponse:
def __init__(self):
self.status_code = 200
self.req_counter = 0
self.client = Client()
self.result = None
@property
def content(self):
self.req_counter += 1
@@ -0,0 +1,111 @@
from django.test import TestCase, Client, override_settings
from django.urls import reverse
import json
from spid_cie_oidc.authority.tests.settings import *
from spid_cie_oidc.entity.models import *
from spid_cie_oidc.onboarding.urls import *
from spid_cie_oidc.entity.jwks import serialize_key
from spid_cie_oidc.entity.jwks import new_rsa_key
from spid_cie_oidc.entity.tests.settings import *
from spid_cie_oidc.authority.models import *
from spid_cie_oidc.authority.tests.mocked_responses import *
from spid_cie_oidc.authority.tests.settings import *
from spid_cie_oidc.onboarding.tests.mocked_responses import *
from unittest.mock import patch
class OnboardingTest(TestCase):
def setUp(self):
self.rp_conf = FederationEntityConfiguration.objects.create(**rp_conf)
self.data = {
"organization_name": "",
"url_entity": "",
"authn_buttons_page_url": "",
"type":"",
"public_jwks": "",
}
@override_settings(HTTP_CLIENT_SYNC=True)
@patch("requests.get", return_value=OnboardingRegistrationResponse())
def test_onboarding_registration(self, mocked):
url = reverse("oidc_onboarding_registration")
client = Client()
res = client.get(url, self.data)
self.assertEqual(res.status_code, 200)
res = client.post(url, self.data)
self.assertFormError(
res, "form", "organization_name", "Enter your organization name"
)
self.assertFormError(res, "form", "url_entity",
"Enter the entity unique identifier (HTTP URL)"
)
self.assertFormError(
res, "form", "public_jwks",
"Enter the public JWKs published in your Entity Configuration, as double check."
)
self.assertEqual(res.status_code, 200)
self.data["organization_name"] = "test name"
self.data["url_entity"] = "http://rp-test/oidc/rp"
self.data["type"] = "openid_relying_party"
self.data["authn_buttons_page_url"] = "https://authnurl.com"
self.data["public_jwks"] = {"key": "ciao"}
res = client.post(url, self.data)
self.assertContains(res, "Inserisci un JSON valido.")
self.assertEqual(res.status_code, 200)
jwk = serialize_key(new_rsa_key().pub_key)
self.data["public_jwks"] = json.dumps(jwk)
# res = client.post(url, self.data)
# self.assertEqual(res.status_code, 200)
# res = client.get(url)
# self.assertEqual(res.status_code, 200)
# self.assertIn(self.data["organization_name"], res.content.decode())
# self.assertIn("acquired", res.content.decode())
def test_schema_authorization(self):
c = Client()
url = reverse("oidc_onboarding_schemas_Authorization")
res = c.get(url)
self.assertTrue(res.status_code == 200)
self.assertIn("Schemas authorization endpoint", res.content.decode())
def test_schema_introspection(self):
c = Client()
url = reverse("oidc_onboarding_schemas_introspection")
res = c.get(url)
self.assertTrue(res.status_code == 200)
self.assertIn("Schemas introspection endpoint", res.content.decode())
def test_schema_metadata(self):
c = Client()
url = reverse("oidc_onboarding_schemas_metadata")
res = c.get(url)
self.assertTrue(res.status_code == 200)
self.assertIn("Schemas metadata Spid/Cie", res.content.decode())
def test_schema_revocation(self):
c = Client()
url = reverse("oidc_onboarding_schemas_revocation")
res = c.get(url)
self.assertTrue(res.status_code == 200)
self.assertIn("Schemas revocation endpoint", res.content.decode())
def test_schema_token(self):
c = Client()
url = reverse("oidc_onboarding_schemas_token")
res = c.get(url)
self.assertTrue(res.status_code == 200)
self.assertIn("Schemas token endpoint", res.content.decode())
def test_schema_client_assertion(self):
c = Client()
url = reverse("oidc_onboarding_schemas_jwt_client_assertion")
res = c.get(url)
self.assertTrue(res.status_code == 200)
self.assertIn("Schemas jwt", res.content.decode())
@@ -0,0 +1,252 @@
import json
from django.test import TestCase, Client
from django.urls import reverse
from copy import deepcopy
from spid_cie_oidc.entity.jwtse import create_jws
from spid_cie_oidc.onboarding.tests.tools_settings import jwk_priv, jwt, jwe
from spid_cie_oidc.entity.tests.settings import ta_conf_data
from spid_cie_oidc.authority.models import (
FederationEntityConfiguration,
FederationEntityProfile,
FederationDescendant,
FederationEntityAssignedProfile
)
from spid_cie_oidc.authority.tests.settings import (
rp_conf,
RP_PROFILE,
rp_onboarding_data
)
from spid_cie_oidc.entity.jwks import (
private_pem_from_jwk,
public_pem_from_jwk
)
from spid_cie_oidc.entity.tests.op_metadata_settings import OP_METADATA_SPID
from spid_cie_oidc.entity.tests.rp_metadata_settings import RP_METADATA_CIE
from spid_cie_oidc.provider.tests.authn_request_settings import AUTHN_REQUEST_SPID
from spid_cie_oidc.authority.tests.settings import RP_METADATA_JWK1, RP_METADATA_JWK1_pub
class ToolsTests(TestCase):
def setUp(self):
self.client = Client()
self.ta_conf = FederationEntityConfiguration.objects.create(**ta_conf_data)
self.rp_conf = FederationEntityConfiguration.objects.create(**rp_conf)
self.rp_profile = FederationEntityProfile.objects.create(**RP_PROFILE)
self.rp = FederationDescendant.objects.create(**rp_onboarding_data)
self.rp_assigned_profile = FederationEntityAssignedProfile.objects.create(
descendant=self.rp, profile=self.rp_profile, issuer=self.ta_conf
)
def test_create_jwk(self):
url = reverse("oidc_onboarding_create_jwk")
res = self.client.get(url)
self.assertEqual(res.status_code, 200)
self.assertIsNotNone(res.context['private_jwk'])
def test_convert_jwk_to_pem(self):
url = reverse("oidc_onboarding_convert_jwk")
res = self.client.get(url)
self.assertEqual(res.status_code, 200)
#check with jwk error
res = self.client.post(url + '?type=private', {'kty': 'RSA'})
self.assertEqual(res.status_code, 200)
self.assertIn("alert-error", res.content.decode())
#check with public jwk the function jwk priv to pem
res = self.client.post(url + '?type=private', {'jwk':json.dumps(RP_METADATA_JWK1_pub)})
self.assertEqual(res.status_code, 200)
self.assertIn("alert-error", res.content.decode())
#check with right jwk private
res = self.client.post(url + '?type=private', {'jwk':json.dumps(RP_METADATA_JWK1)})
self.assertEqual(res.status_code, 200)
self.assertNotIn("alert-error", res.content.decode())
#check with right jwk public
res = self.client.post(url + '?type=public', {'jwk':json.dumps(RP_METADATA_JWK1_pub)})
self.assertEqual(res.status_code, 200)
self.assertNotIn("alert-error", res.content.decode())
def test_convert_pem_to_jwk(self):
url = reverse("oidc_onboarding_convert_pem")
res = self.client.get(url)
self.assertEqual(res.status_code, 200)
pem_private = private_pem_from_jwk(RP_METADATA_JWK1)
res = self.client.post(url + '?type=private', {"pem": pem_private})
self.assertEqual(res.status_code, 200)
self.assertNotIn("alert-error", res.content.decode())
pem_public = public_pem_from_jwk(RP_METADATA_JWK1_pub)
res = self.client.post(url + '?type=public', {"pem": pem_public})
self.assertEqual(res.status_code, 200)
self.assertNotIn("alert-error", res.content.decode())
def test_decode_and_verify_jwt(self):
url = reverse("oidc_onboarding_tools_decode_jwt")
res = self.client.get(url)
self.assertEqual(res.status_code, 200)
res = self.client.post(url, {"jwt":jwt})
self.assertEqual(res.status_code, 200)
self.assertIsNotNone(res.context['payload'])
self.assertIsNotNone(res.context['head'])
jwkError = {"jwkerror":"error"}
jwk = json.dumps(jwkError)
res = self.client.post(url, {"jwt":jwt, "jwk": jwk})
self.assertEqual(res.status_code, 200)
self.assertIn("alert-error", res.content.decode())
jwk = json.dumps(jwk_priv)
res = self.client.post(url, {"jwt":jwt, "jwk": jwk})
self.assertEqual(res.status_code, 200)
self.assertIn("alert-success", res.content.decode())
def test_decode_and_verify_jwe(self):
# first failed because no jwks is submitted
url = reverse("oidc_onboarding_tools_decode_jwt")
res = self.client.get(url)
self.assertEqual(res.status_code, 200)
res = self.client.post(url, {"jwt":jwe})
self.assertEqual(res.status_code, 400)
res = self.client.post(url, {"jwt": jwe, "jwk": json.dumps(jwk_priv)})
self.assertEqual(res.status_code, 200)
def test_resolve_statement(self):
url = reverse("oidc_onboarding_resolve_statement")
res = self.client.get(url)
self.assertEqual(res.status_code, 200)
res = self.client.get(url, {
"sub":"http://127.0.0.1:8000/oidc",
"type": "openid_provider",
"trust_anchor": "http://127.0.0.1:8000"
})
self.assertEqual(res.status_code, 200)
self.assertIn("text-danger", res.content.decode())
# self.assertIn(
# "Failed to resolve entity statement, Please check your inserted data",
# res.content.decode())
form_data= {
"sub": "http://127.0.0.1:8000/oidc/op",
"type": "openid_provider",
"trust_anchor": "http://127.0.0.1:8000",
}
res = self.client.get(url, form_data)
self.assertEqual(res.status_code, 200)
self.assertIsNotNone(res.context['resolved_statement'])
def test_validating_trust_mark(self):
url = reverse("oidc_onboarding_validating_trustmark")
res = self.client.get(url)
self.assertEqual(res.status_code, 200)
res = self.client.get(url, {
"id": "https://www.ciao.gov.it/certification/rp",
"sub": "http://ciao.it/oidc/rp/",
})
self.assertEqual(res.status_code, 200)
self.assertIn("alert-error", res.content.decode())
res = self.client.get(url, {
"id": "https://www.spid.gov.it/certification/rp",
"sub": "http://rp-test.it/oidc/rp/",
})
self.assertEqual(res.status_code, 200)
self.assertIn("alert-success", res.content.decode())
trust_mark = self.rp_assigned_profile.trust_mark_as_jws
res = self.client.get(url, {
"trust_mark": trust_mark,
})
self.assertEqual(res.status_code, 200)
self.assertIn("alert-success", res.content.decode())
def test_validate_metadata(self):
url = reverse("oidc_onboarding_validate_md")
OP_METADATA_SPID_WRONG = deepcopy(OP_METADATA_SPID)
OP_METADATA_SPID_WRONG["issuer"] = "htps://idserver.servizicie.interno.gov.it/op/"
res = self.client.post(url + '?metadata_type=op_metadata&provider_profile=spid',
{'md': json.dumps(OP_METADATA_SPID_WRONG)})
self.assertEqual(res.status_code, 200)
self.assertIn("alert-error", res.content.decode())
res = self.client.post(url + '?metadata_type=op_metadata&provider_profile=spid',
{'md': json.dumps(OP_METADATA_SPID)})
self.assertEqual(res.status_code, 200)
self.assertIn("alert-success", res.content.decode())
RP_METADATA_CIE_WRONG = deepcopy(RP_METADATA_CIE)
RP_METADATA_CIE_WRONG["jwks_uri"] = "htps://registry.cie.gov.it/keys.json"
res = self.client.post(url + '?metadata_type=rp_metadata&provider_profile=cie',
{'md': json.dumps(RP_METADATA_CIE_WRONG)})
self.assertEqual(res.status_code, 200)
self.assertIn("alert-error", res.content.decode())
res = self.client.post(url + '?metadata_type=rp_metadata&provider_profile=cie',
{'md': json.dumps(RP_METADATA_CIE)})
self.assertEqual(res.status_code, 200)
self.assertIn("alert-success", res.content.decode())
res = self.client.post(url + '?metadata_type=rp_metadata&provider_profile=cie',
{'md': "hello world"})
self.assertEqual(res.status_code, 200)
self.assertIn("alert-error", res.content.decode())
def test_validating_trust_mark(self):
url = reverse("oidc_onboarding_validating_trustmark")
res = self.client.post(url)
self.assertEqual(res.status_code, 200)
res = self.client.post(url, data={
"id": "https://www.ciao.gov.it/certification/rp",
"sub": "http://ciao.it/oidc/rp/",
})
self.assertEqual(res.status_code, 200)
self.assertIn("alert-error", res.content.decode())
res = self.client.post(url, data={
"id": "https://www.spid.gov.it/certification/rp",
"sub": "http://rp-test.it/oidc/rp/",
})
self.assertEqual(res.status_code, 200)
self.assertIn("alert-success", res.content.decode())
trust_mark = self.rp_assigned_profile.trust_mark_as_jws
res = self.client.post(url, data={
"trust_mark": trust_mark,
})
self.assertEqual(res.status_code, 200)
self.assertIn("alert-success", res.content.decode())
def test_validate_authn_request(self):
url = reverse("oidc_onboarding_validate_authn_request_jwt")
AUTHN_REQUEST_SPID_WRONG = deepcopy(AUTHN_REQUEST_SPID)
AUTHN_REQUEST_SPID_WRONG["client_id"] = "hps://rp.cie.it/callback1/"
jwt = create_jws(AUTHN_REQUEST_SPID_WRONG, RP_METADATA_JWK1)
res = self.client.post(url + '?provider_profile=spid',
{'md': jwt})
self.assertEqual(res.status_code, 200)
self.assertIn("alert-error", res.content.decode())
jwt = create_jws(AUTHN_REQUEST_SPID, RP_METADATA_JWK1)
res = self.client.post(url + '?provider_profile=spid',
{'md': jwt})
self.assertEqual(res.status_code, 200)
self.assertIn("alert-success", res.content.decode())
@@ -0,0 +1,25 @@
from django.test import TestCase
from spid_cie_oidc.authority.models import FederationDescendant, FederationDescendantContact
from spid_cie_oidc.onboarding.admin import OnBoardingRegistrationAdmin
from spid_cie_oidc.onboarding.models import OnBoardingRegistration
from spid_cie_oidc.authority.tests.settings import RP_METADATA_JWK1_pub, rp_conf
class AdminTest(TestCase):
def setUp(self):
self.onb_regist = OnBoardingRegistration.objects.create(
organization_name = "RP test",
url_entity = rp_conf["sub"],
public_jwks = RP_METADATA_JWK1_pub,
type = "openid_relying_party",
contact = "[email protected]"
)
print(self.onb_regist)
def test_make_published(self):
queryset = [self.onb_regist]
OnBoardingRegistrationAdmin.enable_as_descendant(None, request = {}, queryset = queryset)
self.assertTrue(len(FederationDescendantContact.objects.all()) == 1)
self.assertTrue(len(FederationDescendant.objects.all()) == 1)
@@ -0,0 +1,15 @@
from spid_cie_oidc.entity.jwtse import create_jwe
jwk_priv = {
'kty': 'RSA',
'n': 'wct3JUvUxk0JYnDD0sdy0BkLfiLO_Kfy7hvRp7AC1GlvV74FrDfDnYeTTCDPoXEJN2DMiTnMvWXENaFcBkI1RMSaMqupwp6aoGniFDNCiwEh5wgrjVpaxb5Au4pj4Lq1ecbRDDktwqKDVoQsCXsAAApiTkwjq4g6liH8-PTVG-QCM0gL7VIU0jEKCGX6BuZwuKvfEeNTMboMLnn8_AqdsTtvmDommLaOiOgjU0qS7cvowdVX7fhtDeCgVYvPetAFEvimPAE0i4cQ9RAoqlgFCBpRXz9UyeGAXlaFWtLk_t59jNv4nJEP1gQ2VvAvqA4gOn0f9s7Gf1AsFSxrnmeP1Q',
'e': 'AQAB',
'd': 'vtW1hiyeYogJ7b5Nl4qALRv8jUGJwpNATelnE1Hn93O8CRM-UtC2-LFBA35r9_ZvtPDFeXxJeVh1VrqG_ELGYemYTr1sdhHpAn5B-tJffLKox_Drsk5_hKpM5GDq6anyevClevvza-7Mlq2hD9TnsyKGm8OufIOx7ZWany25jbPK_ksU9X_G9J5RkQ5aDUz7OiUJ-LyL2xmW4gldp_frk_lYgFTgWOp7ZilDQWKYkeLCDzuuRLbM01D7jEJR2c28huQ_vVlw7457NVh-WWyY8Kw0TBgMOU8rrfmgdicv-kbrQaAz60ip95g5FV8ALqSewQYo3v7-4FD8nWVWu01yIQ',
'p': '5Qo4l7oom0P08IBun83TtYOEoDjFEuxsOKcWHfSMYcbMpMCog97BA7ji_0tFHVgqjUAfnEXj4U1yi-ScKZHeiusVYzo5_tQXm8f9bjylw4kOQ1UMSoloz7_sd9xT9jpnWXNxLWHXCoXXWDHUV-iXTewDnRM3McWkA1REIdGogG8',
'q': '2JsvLNvn5O1CAm5bZesbI4WA2Lx30Y0pEfeqMgpsGYg6QSP-zHc1-jJ5D2zhl4W334NeeUx5pSFzQczg7k-jCWrgKD-nvHzjVxOrzKsG4bOUHIqMDaRbeYH9t-BFNy3b8E6O6sqHKWTbd36JFOi7OBZpmtwSVl7JRYr7sXkUDfs',
'kid': 'yxMflR9L6k9Uf-uarpBS3OfgcO1SzbwpZEWbCeOSM3I'
}
jwt = 'eyJhbGciOiJSUzI1NiIsImtpZCI6Inl4TWZsUjlMNms5VWYtdWFycEJTM09mZ2NPMVN6YndwWkVXYkNlT1NNM0kifQ.eyJjbGllbnRfaWQiOiJodHRwczovL3JwLmNpZS5pdCIsInJlc3BvbnNlX3R5cGUiOiJjb2RlIiwic2NvcGUiOiJvcGVuaWQiLCJjb2RlX2NoYWxsZW5nZSI6InFXSmxNZTB4ZGJYckt4VG03MkVwSDY1OWJVeEF4dzgwIiwiY29kZV9jaGFsbGVuZ2VfbWV0aG9kIjoiUzI1NiIsIm5vbmNlIjoiTUJ6R3F5ZjlReXREMjhldXB5V2hTcU1qNzhXTnFwYzIiLCJwcm9tcHQiOiJjb25zZW50IGxvZ2luIiwicmVkaXJlY3RfdXJpIjoiaHR0cHM6Ly9ycC5jaWUuaXQvY2FsbGJhY2sxLyIsImFjcl92YWx1ZXMiOiJDSUVfTDEgQ0lFX0wyIiwiY2xhaW1zIjp7ImlkX3Rva2VuIjp7ImZhbWlseV9uYW1lIjp7ImVzc2VudGlhbCI6dHJ1ZX0sImVtYWlsIjp7ImVzc2VudGlhbCI6dHJ1ZX19LCJ1c2VyaW5mbyI6eyJuYW1lIjpudWxsLCJmYW1pbHlfbmFtZSI6bnVsbH19LCJzdGF0ZSI6ImZ5WmlPTDlMZjJDZUt1TlQySnp4aUxSRGluazB1UGNkIn0.tJqM9mXrg35C78WkCTJQgr7cDirr5mBa22XHDKQI4Bsto5g70LnBJcBN4v85gAqTOYOvhVPcjfN5UKtS2yTnlXMMbWtIjjX3lBlPVTxze5CjdPVKaDf4EB_za2ampPmc2pZh71T9M9exUaSSi1537Sxq9Wbh3QHomtNOoCapp9IS5UheBDe7P3xkU5-pm24NtC3M9EdJHa8AS42EpdbYx5N4md2DaUg0mU1-5sBFLoESVXOj6J1qDQdOqs8Z-wFpjpBi82v4l7woWalzkdVsmeTDxkeb2TCuNAjN29rpgYiVw78WMFi2P5c7t2ZPdQADUdAqpbUvfN4k1hq3Q-WPPA'
jwe = create_jwe(jwt, jwk_priv)
@@ -0,0 +1,145 @@
"""acsia_agent_proj URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/4.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.conf import settings
from django.urls import path
from .views import (
onboarding_landing,
onboarding_registration,
onboarding_entities,
onboarding_create_jwk,
onboarding_convert_jwk,
onboarding_convert_pem,
onboarding_resolve_statement,
onboarding_schemas_federation_entity_endpoints,
onboarding_validating_trustmark,
onboarding_decode_jwt,
onboarding_apply_policy,
onboarding_validate_md,
onboarding_schemas_authorization,
onboarding_schemas_introspection,
onboarding_schemas_metadata,
onboarding_schemas_revocation,
onboarding_schemas_token,
onboarding_schemas_jwt_client_assertion,
onboarding_validate_authn_request,
onboarding_validate_ec,
)
_PREF = getattr(settings, "OIDC_PREFIX", "")
urlpatterns = [
path(
f"{_PREF}onboarding/landing",
onboarding_landing,
name="oidc_onboarding_landing",
),
path(
f"{_PREF}onboarding/registration",
onboarding_registration,
name="oidc_onboarding_registration",
),
path(
f"{_PREF}onboarding/entities",
onboarding_entities,
name="oidc_onboarding_entities",
),
path(
f"{_PREF}onboarding/tools/create-jwk",
onboarding_create_jwk,
name="oidc_onboarding_create_jwk",
),
path(
f"{_PREF}onboarding/tools/convert-jwk",
onboarding_convert_jwk,
name="oidc_onboarding_convert_jwk",
),
path(
f"{_PREF}onboarding/convert-pem",
onboarding_convert_pem,
name="oidc_onboarding_convert_pem",
),
path(
f"{_PREF}onboarding/tools/resolve-statement",
onboarding_resolve_statement,
name="oidc_onboarding_resolve_statement",
),
path(
f"{_PREF}onboarding/tools/validating-trustmark",
onboarding_validating_trustmark,
name="oidc_onboarding_validating_trustmark",
),
path(
f"{_PREF}onboarding/tools/decode-jwt",
onboarding_decode_jwt,
name="oidc_onboarding_tools_decode_jwt",
),
path(
f"{_PREF}onboarding/tools/apply-policy",
onboarding_apply_policy,
name="oidc_onboarding_tools_apply_policy",
),
path(
f"{_PREF}onboarding/schemas/federation_entity",
onboarding_schemas_federation_entity_endpoints,
name="oidc_onboarding_schemas_federation_entity",
),
path(
f"{_PREF}onboarding/schemas/authorization",
onboarding_schemas_authorization,
name="oidc_onboarding_schemas_Authorization",
),
path(
f"{_PREF}onboarding/schemas/introspection",
onboarding_schemas_introspection,
name="oidc_onboarding_schemas_introspection",
),
path(
f"{_PREF}onboarding/schemas/metadata",
onboarding_schemas_metadata,
name="oidc_onboarding_schemas_metadata",
),
path(
f"{_PREF}onboarding/schemas/revocation",
onboarding_schemas_revocation,
name="oidc_onboarding_schemas_revocation",
),
path(
f"{_PREF}onboarding/schemas/token",
onboarding_schemas_token,
name="oidc_onboarding_schemas_token",
),
path(
f"{_PREF}onboarding/schemas/jwt/client/assertion",
onboarding_schemas_jwt_client_assertion,
name="oidc_onboarding_schemas_jwt_client_assertion",
),
path(
f"{_PREF}onboarding/tools/validate-md",
onboarding_validate_md,
name="oidc_onboarding_validate_md",
),
path(
f"{_PREF}onboarding/tools/validate-authn-request",
onboarding_validate_authn_request,
name="oidc_onboarding_validate_authn_request_jwt",
),
path(
f"{_PREF}onboarding/tools/validate-ec",
onboarding_validate_ec,
name="oidc_onboarding_validate_ec",
),
]
@@ -0,0 +1,12 @@
from .models import OnBoardingRegistration
from django.core.exceptions import ValidationError
def unique_entity_url(value): # pragma: no cover
if OnBoardingRegistration.objects.filter(url_entity=value):
raise ValidationError(f"{value} already onboarded")
def trust_anchor_validator(value): # pragma: no cover
pass
# raise ValidationError(f"{value} trust anchor not exist")
@@ -0,0 +1,458 @@
import json
from django.core.paginator import Paginator
from django.shortcuts import render, redirect
from django.contrib import messages
from django.utils.translation import gettext as _
from spid_cie_oidc.authority.schemas.fetch_endpoint_request import FetchRequest
from spid_cie_oidc.authority.schemas.list_endpoint import ListRequest, ListResponse
from spid_cie_oidc.entity.schemas.resolve_endpoint import ResolveRequest, ResolveResponse
from spid_cie_oidc.authority.schemas.trust_mark_status_endpoint import TrustMarkRequest, TrustMarkResponse
from spid_cie_oidc.entity.schemas.fa_metadata import FAMetadata
from .forms import (
OnboardingRegistrationForm,
OnboardingCreateTrustChain,
OnboardingValidatingTrustMarkForm,
OnboardingDecodeForm
)
from .models import OnBoardingRegistration
from spid_cie_oidc.entity.jwks import (
private_pem_from_jwk,
public_pem_from_jwk,
new_rsa_key,
serialize_key,
private_jwk_from_pem,
public_jwk_from_pem
)
from spid_cie_oidc.entity.jwtse import unpad_jwt_head, unpad_jwt_payload, verify_jws, decrypt_jwe
from spid_cie_oidc.authority.views import trust_mark_status
from spid_cie_oidc.authority.validators import validate_entity_configuration
from spid_cie_oidc.provider.schemas.authn_requests import AuthenticationRequestSpid
from spid_cie_oidc.provider.schemas.authn_response import AuthenticationResponse
from spid_cie_oidc.provider.schemas.authn_response import AuthenticationErrorResponse
from spid_cie_oidc.provider.schemas.introspection_request import IntrospectionRequest
from spid_cie_oidc.provider.schemas.introspection_response import IntrospectionResponse
from spid_cie_oidc.provider.schemas.introspection_response import IntrospectionErrorResponseSpid
from spid_cie_oidc.entity.schemas.op_metadata import OPMetadataSpid, OPMetadataCie
from spid_cie_oidc.entity.schemas.rp_metadata import RPMetadataSpid,RPMetadataCie
from spid_cie_oidc.provider.schemas.revocation_request import RevocationRequest
from spid_cie_oidc.provider.schemas.token_requests import TokenAuthnCodeRequest
from spid_cie_oidc.provider.schemas.token_response import TokenResponse
from spid_cie_oidc.provider.schemas.token_response import TokenErrorResponse
from spid_cie_oidc.provider.schemas.token_requests import TokenRefreshRequest
from spid_cie_oidc.provider.schemas.token_response import TokenRefreshResponse
from spid_cie_oidc.provider.schemas.jwt import JwtStructure
from spid_cie_oidc.entity.policy import apply_policy
from spid_cie_oidc.relying_party.settings import RP_PROVIDER_PROFILES
from spid_cie_oidc.provider.settings import OIDCFED_PROVIDER_PROFILES
from spid_cie_oidc.entity.views import resolve_entity_statement
def onboarding_landing(request): # pragma: no cover
return render(request, "onboarding_landing.html")
def onboarding_registration(request):
form = OnboardingRegistrationForm()
context = {"form": form}
if request.method == "POST":
form = OnboardingRegistrationForm(request.POST)
if not form.is_valid():
context = {"form": form}
else:
form_dict = {**form.cleaned_data}
OnBoardingRegistration.objects.create(**form_dict)
messages.success(request, _("Registration successfully"))
return redirect("oidc_onboarding_entities")
return render(request, "onboarding_registration.html", context)
def onboarding_entities(request):
entity_list = OnBoardingRegistration.objects.all()
p = Paginator(entity_list, 10)
page = request.GET.get("page")
entities = p.get_page(page)
return render(
request,
"onboarding_entities.html",
{"entity_list": entity_list, "entities": entities},
)
def onboarding_create_jwk(request):
_rsa_key = new_rsa_key()
private_jwk = serialize_key(_rsa_key.priv_key, 'private')
public_jwk = serialize_key(_rsa_key.pub_key)
context = {
"private_jwk": json.dumps(private_jwk, indent=4),
"public_jwk": json.dumps(public_jwk, indent=4)
}
return render(request, 'onboarding_jwk.html', context)
def onboarding_convert_jwk(request):
jwk_type = request.GET.get('type')
context = {
"jwk": "",
"pem": "",
"jwk_type": jwk_type
}
if request.method == 'POST':
try:
jwk_str = request.POST.get('jwk')
jwk_str_double_quote = jwk_str.replace("'", '"')
jwk_dict = json.loads(jwk_str_double_quote)
if jwk_type == 'private':
pem = private_pem_from_jwk(jwk_dict)
if jwk_type == 'public':
pem = public_pem_from_jwk(jwk_dict)
context = {
"jwk": jwk_dict,
"pem": pem,
"jwk_type": jwk_type
}
except Exception as e:
messages.error(request, _(f" {e} "))
return render(request, 'onboarding_convert_jwk.html', context)
def onboarding_convert_pem(request):
pem_type = request.GET.get('type')
context = {
"pem_type": pem_type
}
if request.method == 'POST':
try:
pem = request.POST.get('pem')
if pem_type == 'private':
jwk = private_jwk_from_pem(pem)
if pem_type == 'public':
jwk = public_jwk_from_pem(pem)
context = {
"pem": pem,
"pem_type": pem_type,
"jwk": jwk
}
except Exception as e:
messages.error(request, _(f" {e} "))
return render(request, 'onboarding_convert_pem.html', context)
def onboarding_resolve_statement(request):
if "sub" in request.GET :
form = OnboardingCreateTrustChain(request.GET)
else:
form = OnboardingCreateTrustChain()
context = {'form': form}
if form.is_valid():
context = {
'form': form,
"resolved_statement": "",
}
try:
res = resolve_entity_statement(request, format="json")
resultJson = json.loads(res.content.decode())
context["resolved_statement"] = json.dumps(resultJson, indent=4)
except Exception:
messages.error(request, _('Failed to resolve entity statement, Please check your inserted data'))
return render(request, 'onboarding_resolve_statement.html', context)
def onboarding_validating_trustmark(request):
if "id" in request.POST or "trust_mark" in request.POST:
form = OnboardingValidatingTrustMarkForm(request.POST)
else:
form = OnboardingValidatingTrustMarkForm()
context = {"form": form}
if form.is_valid():
res = trust_mark_status(request)
body = res.content.decode()
context = {'form': form}
ct = res.get("Content-Type", "")
if res.status_code == 200 and ct.startswith("application/trust-mark-status-response+jwt"):
# Draft 48: response is a signed JWT with status claim
from spid_cie_oidc.entity.jwtse import unpad_jwt_payload
content = unpad_jwt_payload(body)
active = content.get("status") == "active"
else:
# Error response (e.g. 404) or legacy JSON body
try:
content = json.loads(body)
active = content.get("active", False)
except (json.JSONDecodeError, TypeError):
active = False
if active:
messages.success(request, _('Validation Trust Mark Successfully'))
else:
messages.error(request, _('Validation Trust Mark Failed'))
return render(request, 'onboarding_validating_tm.html', context)
def onboarding_validate_md(request):
metadata_type = request.GET.get('metadata_type')
provider_profile = request.GET.get('provider_profile')
md_type = metadata_type.replace("_", " ")
title = f'Validate {md_type} {provider_profile}'
description = f'Enter a {md_type} of {provider_profile} to verify if it is compatible'
context = {
"metadata_type": metadata_type,
"provider_profile": provider_profile,
"title": title,
"description": description,
"field_name":"metadata"
}
if request.POST.get('md'):
md = request.POST['md']
context["md"] = md
try:
md_str_double_quote = md.replace("'", '"')
metadata = json.loads(md_str_double_quote)
if metadata_type == 'op_metadata':
schema = OIDCFED_PROVIDER_PROFILES[provider_profile]
schema[metadata_type](**metadata)
messages.success(request, _('Validation Metadata Successfully'))
if metadata_type == 'rp_metadata':
schema = RP_PROVIDER_PROFILES[provider_profile]
schema[metadata_type](**metadata)
messages.success(request, _('Validation Metadata Successfully'))
except Exception as e:
messages.error(request, f"Validation Failed: {e}")
return render(request, 'onboarding_validate_md.html', context)
def onboarding_validate_authn_request(request):
provider_profile = request.GET.get('provider_profile')
title = f'Validate authn request {provider_profile}'
description = f'Enter a jwt of authn request for {provider_profile} to verify if it is compatible'
context = {
"provider_profile": provider_profile,
"title": title,
"description": description,
"field_name":"jwt"
}
if request.POST.get('md'):
jwt_str = request.POST['md']
context = {
"provider_profile": provider_profile,
"title": title,
"description": description,
"field_name":"jwt",
"md": jwt_str
}
payload = unpad_jwt_payload(jwt_str)
schema = OIDCFED_PROVIDER_PROFILES[provider_profile]
try:
schema["authorization_request"](**payload)
messages.success(request, _('Validation Authn Request Successfully'))
except Exception as e:
messages.error(request, f"Validation Failed: {e}")
return render(request, 'onboarding_validate_md.html', context)
def onboarding_validate_ec(request):
context = {}
if request.POST:
url = request.POST.get("url")
context = {"url": url}
try:
ec = validate_entity_configuration(url)
context["ec"] = json.dumps(ec.payload, indent=4)
messages.success(request, _('Validation Entity Configuration Successfully'))
except Exception as e :
messages.error(request, f"Validation Failed: {e}")
return render(request, 'onboarding_validate_ec.html', context)
return render(request, 'onboarding_validate_ec.html', context)
def onboarding_decode_jwt(request):
if request.method == "GET":
form = OnboardingDecodeForm()
else:
form = OnboardingDecodeForm(request.POST)
context = {'form': form}
if form.is_valid():
form_dict = {**form.cleaned_data}
jwt = form_dict['jwt']
try:
head = unpad_jwt_head(jwt)
except Exception as e:
messages.error(
request,
f"JWS verification failed due to missing JWT header: {e}"
)
return render(request, "onboarding_decode_jwt.html", context)
if head.get('enc', None):
if not form_dict.get('jwk'):
messages.error(
request,
"JWE needs a private jwk to be decrypted"
)
return render(
request, "onboarding_decode_jwt.html", context, status = 400
)
# JWE here
jwk = form_dict['jwk']
jwt = decrypt_jwe(jwt, jwk)
try:
payload = unpad_jwt_payload(jwt)
context["head"] = json.dumps(head, indent=4)
context["payload"] = json.dumps(payload, indent=4)
jwk = form_dict["jwk"]
if jwk:
verify_jws(jwt, jwk)
messages.success(request, _('Your jws is verified'))
except Exception as e:
messages.error(request, f"JWS verification failed: {e}")
return render(request, "onboarding_decode_jwt.html", context)
def onboarding_apply_policy(request):
context = {
"md": "",
"policy": "",
"result": ""
}
if request.GET.get('md') and request.GET.get('policy'):
context = {
"md": request.GET['md'],
"policy": request.GET['policy'],
"result": ""
}
try:
md = json.loads(request.GET['md'])
policy = json.loads(request.GET['policy'])
reuslt = apply_policy(md, policy)
context["result"] = json.dumps(reuslt, indent=4)
except Exception as e:
messages.error(request, {e})
return render(request, 'onboarding_apply_policy.html', context)
def onboarding_schemas_authorization(request):
auth_request = AuthenticationRequestSpid.schema_json(indent=2)
auth_res_succ = AuthenticationResponse.schema_json(indent=2)
auth_res_err = AuthenticationErrorResponse.schema_json(indent=2)
content = {
"title": "Schemas authorization endpoint",
"schemas": {
"authorization request": auth_request,
"authorization successful response": auth_res_succ,
"authorization error response": auth_res_err,
}
}
return render(request, "onboarding_schemas.html", content)
def onboarding_schemas_introspection(request):
intro_request = IntrospectionRequest.schema_json(indent=2)
intro_res_succ = IntrospectionResponse.schema_json(indent=2)
intro_res_err = IntrospectionErrorResponseSpid.schema_json(indent=2)
content = {
"title": "Schemas introspection endpoint",
"schemas": {
"introspection request": intro_request,
"introspection successful response": intro_res_succ,
"introspection error response": intro_res_err,
}
}
return render(request, "onboarding_schemas.html", content)
def onboarding_schemas_federation_entity_endpoints(request):
federation_entity_fetch_request = FetchRequest.schema_json(indent=2)
federation_entity_list_request = ListRequest.schema_json(indent=2)
federation_entity_list_response = ListResponse.schema_json(indent=2)
federation_entity_resolve_request = ResolveRequest.schema_json(indent=2)
federation_entity_resolve_response = ResolveResponse.schema_json(indent=2)
federation_entity_status_request = TrustMarkRequest.schema_json(indent=2)
federation_entity_status_response = TrustMarkResponse.schema_json(indent=2)
content = {
"title": "Schemas Federation entity endpoint",
"schemas": {
"Federation entity fetch": federation_entity_fetch_request,
"Federation entity list request": federation_entity_list_request,
"Federation entity list response": federation_entity_list_response,
"Federation entity resolve request": federation_entity_resolve_request,
"Federation entity resolve response": federation_entity_resolve_response,
"Federation entity status request": federation_entity_status_request,
"Federation entity status response": federation_entity_status_response
}
}
return render(request, "onboarding_schemas.html", content)
def onboarding_schemas_metadata(request):
op_meta_spid = OPMetadataSpid.schema_json(indent=2)
op_meta_cie = OPMetadataCie.schema_json(indent=2)
rp_meta_spid = RPMetadataSpid.schema_json(indent=2)
rp_meta_cie = RPMetadataCie.schema_json(indent=2)
fed_entity = FAMetadata.schema_json(indent=2)
content = {
"title": "Schemas metadata Spid/Cie",
"schemas": {
"Federation entity": fed_entity,
"OP Spid": op_meta_spid,
"OP Cie": op_meta_cie,
"RP Spid": rp_meta_spid,
"RP Cie": rp_meta_cie
}
}
return render(request, "onboarding_schemas.html", content)
def onboarding_schemas_revocation(request):
revoc_request = RevocationRequest.schema_json(indent=2)
content = {
"title": "Schemas revocation endpoint",
"schemas": {
"revocation request": revoc_request
}
}
return render(request, "onboarding_schemas.html", content)
def onboarding_schemas_token(request):
auth_code_req = TokenAuthnCodeRequest.schema_json(indent=2)
auth_code_res = TokenResponse.schema_json(indent=2)
refr_req = TokenRefreshRequest.schema_json(indent=2)
refr_res = TokenRefreshResponse.schema_json(indent=2)
err_res = TokenErrorResponse.schema_json(indent=2)
content = {
"title": "Schemas token endpoint",
"schemas": {
"token authn code request": auth_code_req,
"token authn code response": auth_code_res,
"refresh token request": refr_req,
"refresh token response": refr_res,
"token error response": err_res
}
}
return render(request, "onboarding_schemas.html", content)
def onboarding_schemas_jwt_client_assertion(request):
jwt_client_asser = JwtStructure.schema_json(indent=2)
content = {
"title": "Schemas jwt",
"schemas": {
"jwt client assertion": jwt_client_asser
}
}
return render(request, "onboarding_schemas.html", content)