Skip to content

Commit

Permalink
Merge pull request #4 from BeryJu/propertymapping-jinja
Browse files Browse the repository at this point in the history
PropertyMappings using Jinja
  • Loading branch information
BeryJu authored Feb 17, 2020
2 parents a5629c5 + 3aa2f1e commit 49e915f
Show file tree
Hide file tree
Showing 25 changed files with 421 additions and 273 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
restore-keys: |
${{ runner.os }}-pipenv-
- name: Install dependencies
run: pip install -U pip pipenv && pipenv install --dev
run: pip install -U pip pipenv && pipenv install --dev && pipenv install --dev prospector --skip-lock
- name: Lint with prospector
run: pipenv run prospector
bandit:
Expand Down
2 changes: 1 addition & 1 deletion Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ signxml = "*"
structlog = "*"
swagger-spec-validator = "*"
urllib3 = {extras = ["secure"],version = "*"}
jinja2 = "*"

[requires]
python_version = "3.8"
Expand All @@ -51,7 +52,6 @@ bumpversion = "*"
colorama = "*"
coverage = "*"
django-debug-toolbar = "*"
prospector = "*"
pylint = "*"
pylint-django = "*"
unittest-xml-reporting = "*"
Expand Down
324 changes: 132 additions & 192 deletions Pipfile.lock

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions docs/reference/property-mappings/user-object.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Passbook User Object

The User object has the following attributes:

- `username`: User's Username
- `email` User's E-Mail
- `name` User's Display Name
- `is_staff` Boolean field if user is staff
- `is_active` Boolean field if user is active
- `date_joined` Date User joined/was created
- `password_change_date` Date Password was last changed
- `attributes` Dynamic Attributes

## Examples

List all the User's Group Names

```jinja2
[{% for group in user.groups.all() %}'{{ group.name }}',{% endfor %}]
```
3 changes: 3 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ nav:
- Rancher: integrations/services/rancher/index.md
- Harbor: integrations/services/harbor/index.md
- Sentry: integrations/services/sentry/index.md
- Reference:
- Property Mappings:
- User Object: reference/property-mappings/user-object.md

repo_name: "BeryJu.org/passbook"
repo_url: https://github.com/BeryJu/passbook
Expand Down
2 changes: 1 addition & 1 deletion passbook/admin/templates/generic/create.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{% extends "generic/form.html" %}
{% extends base_template|default:"generic/form.html" %}

{% load utils %}
{% load i18n %}
Expand Down
21 changes: 17 additions & 4 deletions passbook/admin/templates/generic/form.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<link rel="stylesheet" href="{% static 'codemirror/lib/codemirror.css' %}">
<link rel="stylesheet" href="{% static 'codemirror/theme/monokai.css' %}">
<script src="{% static 'codemirror/mode/yaml/yaml.js' %}"></script>
<script src="{% static 'codemirror/mode/jinja2/jinja2.js' %}"></script>
{% endblock %}

{% block content %}
Expand All @@ -29,21 +30,33 @@
<div class="">
<form action="" method="post" class="form-horizontal">
{% include 'partials/form.html' with form=form %}
{% block beneath_form %}
{% endblock %}
<a class="btn btn-default" href="{% back %}">{% trans "Cancel" %}</a>
<input type="submit" class="btn btn-primary" value="{% block action %}{% endblock %}" />
</form>
</div>
{% block beneath_form %}
{% endblock %}
<script>
let attributes = document.getElementsByName('attributes');
const attributes = document.getElementsByName('attributes');
if (attributes.length > 0) {
let myCodeMirror = CodeMirror.fromTextArea(attributes[0], {
// https://github.com/codemirror/CodeMirror/issues/5092
attributes[0].removeAttribute("required");
const attributesCM = CodeMirror.fromTextArea(attributes[0], {
mode: 'yaml',
theme: 'monokai',
lineNumbers: true,
});
}
const templates = document.getElementsByName('template');
if (templates.length > 0) {
// https://github.com/codemirror/CodeMirror/issues/5092
templates[0].removeAttribute("required");
const templateCM = CodeMirror.fromTextArea(templates[0], {
mode: 'jinja2',
theme: 'monokai',
lineNumbers: true,
});
}
</script>
</div>
{% endblock %}
Expand Down
2 changes: 1 addition & 1 deletion passbook/admin/templates/generic/update.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{% extends "generic/form.html" %}
{% extends base_template|default:"generic/form.html" %}

{% load utils %}
{% load i18n %}
Expand Down
9 changes: 9 additions & 0 deletions passbook/admin/views/property_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ def get_context_data(self, **kwargs):
if x.__name__ == property_mapping_type
)
kwargs["type"] = model._meta.verbose_name
form_cls = self.get_form_class()
if hasattr(form_cls, "template_name"):
kwargs["base_template"] = form_cls.template_name
return kwargs

def get_form_class(self):
Expand All @@ -92,6 +95,12 @@ class PropertyMappingUpdateView(
success_url = reverse_lazy("passbook_admin:property-mappings")
success_message = _("Successfully updated Property Mapping")

def get_context_data(self, **kwargs):
form_cls = self.get_form_class()
if hasattr(form_cls, "template_name"):
kwargs["base_template"] = form_cls.template_name
return kwargs

def get_form_class(self):
form_class_path = self.get_object().form
form_class = path_to_class(form_class_path)
Expand Down
19 changes: 19 additions & 0 deletions passbook/core/migrations/0006_propertymapping_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 3.0.3 on 2020-02-17 16:15

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("passbook_core", "0005_merge_20191025_2022"),
]

operations = [
migrations.AddField(
model_name="propertymapping",
name="template",
field=models.TextField(default=""),
preserve_default=False,
),
]
16 changes: 16 additions & 0 deletions passbook/core/migrations/0007_auto_20200217_1934.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Generated by Django 3.0.3 on 2020-02-17 19:34

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("passbook_core", "0006_propertymapping_template"),
]

operations = [
migrations.RenameField(
model_name="propertymapping", old_name="template", new_name="expression",
),
]
13 changes: 11 additions & 2 deletions passbook/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@
from datetime import timedelta
from random import SystemRandom
from time import sleep
from typing import Optional
from typing import Optional, Any
from uuid import uuid4

from jinja2.nativetypes import NativeEnvironment
from django.contrib.auth.models import AbstractUser
from django.contrib.postgres.fields import JSONField
from django.db import models
from django.urls import reverse_lazy
from django.http import HttpRequest
from django.utils.timezone import now
from django.utils.translation import gettext as _
from django.utils.translation import gettext_lazy as _
from django_prometheus.models import ExportModelOperationsMixin
from guardian.mixins import GuardianUserMixin
from model_utils.managers import InheritanceManager
Expand All @@ -22,6 +24,7 @@
from passbook.policies.struct import PolicyRequest, PolicyResult

LOGGER = get_logger()
NATIVE_ENVIRONMENT = NativeEnvironment()


def default_nonce_duration():
Expand Down Expand Up @@ -293,10 +296,16 @@ class PropertyMapping(UUIDModel):
"""User-defined key -> x mapping which can be used by providers to expose extra data."""

name = models.TextField()
expression = models.TextField()

form = ""
objects = InheritanceManager()

def evaluate(self, user: User, request: HttpRequest, **kwargs) -> Any:
"""Evaluate `self.expression` using `**kwargs` as Context."""
expression = NATIVE_ENVIRONMENT.from_string(self.expression)
return expression.render(user=user, request=request, **kwargs)

def __str__(self):
return f"Property Mapping {self.name}"

Expand Down
12 changes: 8 additions & 4 deletions passbook/providers/saml/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ class Meta:
fields = [
"pk",
"name",
"property_mappings",
"processor_path",
"acs_url",
"audience",
"processor_path",
"issuer",
"assertion_valid_for",
"assertion_valid_not_before",
"assertion_valid_not_on_or_after",
"session_valid_not_on_or_after",
"property_mappings",
"digest_algorithm",
"signature_algorithm",
"signing",
"signing_cert",
"signing_key",
Expand All @@ -39,7 +43,7 @@ class SAMLPropertyMappingSerializer(ModelSerializer):
class Meta:

model = SAMLPropertyMapping
fields = ["pk", "name", "saml_name", "friendly_name", "values"]
fields = ["pk", "name", "saml_name", "friendly_name", "expression"]


class SAMLPropertyMappingViewSet(ModelViewSet):
Expand Down
6 changes: 3 additions & 3 deletions passbook/providers/saml/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.utils.translation import gettext as _

from passbook.lib.fields import DynamicArrayField
from passbook.providers.saml.models import (
SAMLPropertyMapping,
SAMLProvider,
Expand Down Expand Up @@ -60,13 +59,14 @@ class Meta:
class SAMLPropertyMappingForm(forms.ModelForm):
"""SAML Property Mapping form"""

template_name = "saml/idp/property_mapping_form.html"

class Meta:

model = SAMLPropertyMapping
fields = ["name", "saml_name", "friendly_name", "values"]
fields = ["name", "saml_name", "friendly_name", "expression"]
widgets = {
"name": forms.TextInput(),
"saml_name": forms.TextInput(),
"friendly_name": forms.TextInput(),
}
field_classes = {"values": DynamicArrayField}
41 changes: 0 additions & 41 deletions passbook/providers/saml/migrations/0003_auto_20200216_1109.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,6 @@
from django.db import migrations, models


def create_default_property_mappings(apps, schema_editor):
"""Create default SAML Property Mappings"""
SAMLPropertyMapping = apps.get_model(
"passbook_providers_saml", "SAMLPropertyMapping"
)
db_alias = schema_editor.connection.alias
defaults = [
{
"FriendlyName": "eduPersonPrincipalName",
"Name": "urn:oid:1.3.6.1.4.1.5923.1.1.1.6",
"Value": "{user.email}",
},
{"FriendlyName": "cn", "Name": "urn:oid:2.5.4.3", "Value": "{user.name}",},
{
"FriendlyName": "mail",
"Name": "urn:oid:0.9.2342.19200300.100.1.3",
"Value": "{user.email}",
},
{
"FriendlyName": "displayName",
"Name": "urn:oid:2.16.840.1.113730.3.1.241",
"Value": "{user.username}",
},
{
"FriendlyName": "uid",
"Name": "urn:oid:0.9.2342.19200300.100.1.1",
"Value": "{user.pk}",
},
]
for default in defaults:
SAMLPropertyMapping.objects.using(db_alias).get_or_create(
saml_name=default["Name"],
friendly_name=default["FriendlyName"],
values=[default["Value"]],
defaults={
"name": f"Autogenerated SAML Mapping: {default['FriendlyName']} -> {default['Value']}"
},
)


class Migration(migrations.Migration):

dependencies = [
Expand Down Expand Up @@ -75,5 +35,4 @@ class Migration(migrations.Migration):
name="signing_cert",
field=models.TextField(verbose_name="Singing Certificate"),
),
migrations.RunPython(create_default_property_mappings),
]
Loading

0 comments on commit 49e915f

Please sign in to comment.