From d1dcdbe4b0a0d662bb4c0dc865f98e6681a7a2de Mon Sep 17 00:00:00 2001
From: Serghei Mihai <smihai@entrouvert.com>
Date: Thu, 10 Mar 2022 17:39:47 +0100
Subject: [PATCH 1/2] misc: add logo and text color for service and OU

---
 .../migrations/0027_auto_20220329_1259.py     | 23 +++++++++++++++++++
 src/authentic2/a2_rbac/models.py              |  2 ++
 src/authentic2/manager/forms.py               |  3 +++
 .../migrations/0037_auto_20220329_1512.py     | 23 +++++++++++++++++++
 src/authentic2/models.py                      |  2 ++
 tests/idp_oidc/test_misc.py                   | 10 +++++---
 6 files changed, 60 insertions(+), 3 deletions(-)
 create mode 100644 src/authentic2/a2_rbac/migrations/0027_auto_20220329_1259.py
 create mode 100644 src/authentic2/migrations/0037_auto_20220329_1512.py

diff --git a/src/authentic2/a2_rbac/migrations/0027_auto_20220329_1259.py b/src/authentic2/a2_rbac/migrations/0027_auto_20220329_1259.py
new file mode 100644
index 000000000..1c3b374f1
--- /dev/null
+++ b/src/authentic2/a2_rbac/migrations/0027_auto_20220329_1259.py
@@ -0,0 +1,23 @@
+# Generated by Django 2.2.24 on 2022-03-10 10:59
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('a2_rbac', '0026_add_roleparenting_soft_delete'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='organizationalunit',
+            name='logo',
+            field=models.ImageField(blank=True, upload_to='services/logos', verbose_name='Logo'),
+        ),
+        migrations.AddField(
+            model_name='organizationalunit',
+            name='text_color',
+            field=models.CharField(blank=True, max_length=7, null=True, verbose_name='Colour'),
+        ),
+    ]
diff --git a/src/authentic2/a2_rbac/models.py b/src/authentic2/a2_rbac/models.py
index 48d150a9f..eeddebe10 100644
--- a/src/authentic2/a2_rbac/models.py
+++ b/src/authentic2/a2_rbac/models.py
@@ -113,6 +113,8 @@ class OrganizationalUnit(OrganizationalUnitAbstractBase):
         default=760,  # two years + 1 month
     )
     home_url = models.URLField(verbose_name=_('Home URL'), max_length=256, null=True, blank=True)
+    logo = models.ImageField(verbose_name=_('Logo'), blank=True, upload_to='services/logos')
+    text_color = models.CharField(verbose_name=_('Colour'), null=True, blank=True, max_length=7)
 
     objects = managers.OrganizationalUnitManager()
 
diff --git a/src/authentic2/manager/forms.py b/src/authentic2/manager/forms.py
index b6163d6f1..ce346f929 100644
--- a/src/authentic2/manager/forms.py
+++ b/src/authentic2/manager/forms.py
@@ -633,6 +633,7 @@ class OUEditForm(SlugMixin, CssClass, forms.ModelForm):
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
         self.fields['name'].label = _('label').title()
+        self.fields['text_color'].widget = forms.TextInput(attrs={'type': 'color'})
 
     class Meta:
         model = OrganizationalUnit
@@ -650,6 +651,8 @@ class OUEditForm(SlugMixin, CssClass, forms.ModelForm):
             'clean_unused_accounts_alert',
             'clean_unused_accounts_deletion',
             'home_url',
+            'logo',
+            'text_color',
         )
 
 
diff --git a/src/authentic2/migrations/0037_auto_20220329_1512.py b/src/authentic2/migrations/0037_auto_20220329_1512.py
new file mode 100644
index 000000000..b349afeb2
--- /dev/null
+++ b/src/authentic2/migrations/0037_auto_20220329_1512.py
@@ -0,0 +1,23 @@
+# Generated by Django 2.2.24 on 2022-03-29 13:12
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('authentic2', '0036_service_profile_types'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='service',
+            name='logo',
+            field=models.ImageField(blank=True, upload_to='services/logos', verbose_name='Logo'),
+        ),
+        migrations.AddField(
+            model_name='service',
+            name='text_color',
+            field=models.CharField(blank=True, max_length=7, null=True, verbose_name='Colour'),
+        ),
+    ]
diff --git a/src/authentic2/models.py b/src/authentic2/models.py
index 1aa2dc055..e43477ed7 100644
--- a/src/authentic2/models.py
+++ b/src/authentic2/models.py
@@ -382,6 +382,8 @@ class Service(models.Model):
         verbose_name=_('callback url when unauthorized'), max_length=256, null=True, blank=True
     )
     home_url = models.URLField(verbose_name=_('Home URL'), max_length=256, null=True, blank=True)
+    logo = models.ImageField(verbose_name=_('Logo'), blank=True, upload_to='services/logos')
+    text_color = models.CharField(verbose_name=_('Colour'), null=True, blank=True, max_length=7)
 
     profile_types = models.ManyToManyField(
         to='custom_user.ProfileType',
diff --git a/tests/idp_oidc/test_misc.py b/tests/idp_oidc/test_misc.py
index 0f18cd223..4d49ab862 100644
--- a/tests/idp_oidc/test_misc.py
+++ b/tests/idp_oidc/test_misc.py
@@ -79,12 +79,13 @@ OIDC_CLIENT_PARAMS = [
         'authorization_mode': OIDCClient.AUTHORIZATION_MODE_NONE,
     },
     {
-        'idtoken_duration': datetime.timedelta(hours=1),
+        'idtoken_duration': '3600',
     },
     {
         'authorization_flow': OIDCClient.FLOW_IMPLICIT,
-        'idtoken_duration': datetime.timedelta(hours=1),
+        'idtoken_duration': '3600',
         'post_logout_redirect_uris': 'https://example.com/',
+        'home_url': 'https://example.com/',
     },
     {
         'frontchannel_logout_uri': 'https://example.com/southpark/logout/',
@@ -92,6 +93,7 @@ OIDC_CLIENT_PARAMS = [
     {
         'frontchannel_logout_uri': 'https://example.com/southpark/logout/',
         'frontchannel_timeout': 3000,
+        'text_color': '#ff00ff',
     },
     {
         'identifier_policy': OIDCClient.POLICY_PAIRWISE_REVERSIBLE,
@@ -260,7 +262,9 @@ def test_authorization_code_sso(
     exp_delta = (parse_timestamp(claims['exp']) - now()).total_seconds()
     assert exp_delta > 0
     if oidc_client.idtoken_duration:
-        assert abs(exp_delta - oidc_client.idtoken_duration.total_seconds()) < 2
+        assert (
+            abs(exp_delta - datetime.timedelta(seconds=int(oidc_client.idtoken_duration)).total_seconds()) < 2
+        )
     else:
         assert abs(exp_delta - 30) < 2
 
-- 
2.35.1

