misc: add logo and text color for service and OU (#47406)

This commit is contained in:
Serghei Mihai 2022-03-10 17:39:47 +01:00
parent 5e772e6b1e
commit 10366ac244
9 changed files with 113 additions and 1 deletions

View File

@ -78,6 +78,8 @@ class OrganizationalUnitAdmin(admin.ModelAdmin):
'user_can_reset_password',
'user_add_password_policy',
'home_url',
'logo',
'colour',
)
readonly_fields = ('uuid',)
prepopulated_fields = {"slug": ("name",)}

View File

@ -0,0 +1,31 @@
# Generated by Django 2.2.24 on 2022-03-31 13:21
from django.db import migrations, models
import authentic2.validators
class Migration(migrations.Migration):
dependencies = [
('a2_rbac', '0026_add_roleparenting_soft_delete'),
]
operations = [
migrations.AddField(
model_name='organizationalunit',
name='colour',
field=models.CharField(
blank=True,
max_length=32,
null=True,
validators=[authentic2.validators.HexaColourValidator()],
verbose_name='Colour',
),
),
migrations.AddField(
model_name='organizationalunit',
name='logo',
field=models.ImageField(blank=True, upload_to='services/logos', verbose_name='Logo'),
),
]

View File

@ -14,6 +14,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
from collections import namedtuple
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
@ -28,6 +29,7 @@ from django.utils.translation import ugettext_lazy as _
from authentic2.decorators import errorcollector
from authentic2.utils.cache import GlobalCache
from authentic2.validators import HexaColourValidator
from django_rbac import utils as rbac_utils
from django_rbac.models import (
VIEW_OP,
@ -113,6 +115,10 @@ 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')
colour = models.CharField(
verbose_name=_('Colour'), null=True, blank=True, max_length=32, validators=[HexaColourValidator()]
)
objects = managers.OrganizationalUnitManager()
@ -166,6 +172,9 @@ class OrganizationalUnit(OrganizationalUnitAbstractBase):
)
def delete(self, *args, **kwargs):
if self.logo and os.path.exists(self.logo.path):
os.unlink(self.logo.path)
Permission.objects.filter(ou=self).delete()
return super(OrganizationalUnitAbstractBase, self).delete(*args, **kwargs)

View File

@ -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['colour'].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',
'colour',
)

View File

@ -0,0 +1,31 @@
# Generated by Django 2.2.24 on 2022-03-31 13:13
from django.db import migrations, models
import authentic2.validators
class Migration(migrations.Migration):
dependencies = [
('authentic2', '0036_service_profile_types'),
]
operations = [
migrations.AddField(
model_name='service',
name='colour',
field=models.CharField(
blank=True,
max_length=32,
null=True,
validators=[authentic2.validators.HexaColourValidator()],
verbose_name='Colour',
),
),
migrations.AddField(
model_name='service',
name='logo',
field=models.ImageField(blank=True, upload_to='services/logos', verbose_name='Logo'),
),
]

View File

@ -15,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import datetime
import os
import time
import urllib.parse
import uuid
@ -37,6 +38,7 @@ from model_utils.managers import QueryManager
from authentic2.a2_rbac.models import Role
from authentic2.utils.crypto import base64url_decode, base64url_encode
from authentic2.validators import HexaColourValidator
# install our natural_key implementation
from . import managers
@ -382,6 +384,10 @@ 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')
colour = models.CharField(
verbose_name=_('Colour'), null=True, blank=True, max_length=32, validators=[HexaColourValidator()]
)
profile_types = models.ManyToManyField(
to='custom_user.ProfileType',
@ -470,6 +476,12 @@ class Service(models.Model):
urls.update(service.get_base_urls())
return list(urls)
def delete(self, *args, **kwargs):
if self.logo and os.path.exists(self.logo.path):
os.unlink(self.logo.path)
return super().delete(*args, **kwargs)
Service._meta.natural_key = [['slug', 'ou']]

View File

@ -124,3 +124,12 @@ class ProhibitNullCharactersValidator:
def __eq__(self, other):
return isinstance(other, self.__class__) and self.message == other.message and self.code == other.code
class HexaColourValidator(RegexValidator):
"""Validates that the string is a hexadecimal colour"""
def __init__(self, *args, **kwargs):
self.regex = '#[0-9a-fA-F]{6}'
self.message = _('Hexadecimal value only allowed.')
super().__init__(*args, **kwargs)

View File

@ -85,6 +85,7 @@ OIDC_CLIENT_PARAMS = [
'authorization_flow': OIDCClient.FLOW_IMPLICIT,
'idtoken_duration': datetime.timedelta(hours=1),
'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,
'colour': '#ff00ff',
},
{
'identifier_policy': OIDCClient.POLICY_PAIRWISE_REVERSIBLE,
@ -120,6 +122,8 @@ def test_admin(other_attributes, app, superuser, oidc_settings):
response.form.set('unauthorized_url', 'https://example.com/southpark/')
response.form.set('redirect_uris', 'https://example.com/callbac%C3%A9')
for key, value in other_attributes.items():
if isinstance(value, datetime.timedelta):
value = f'{value.total_seconds()}'
response.form.set(key, value)
response = response.form.submit().follow()
assert OIDCClient.objects.count() == 1

View File

@ -21,7 +21,7 @@ from unittest import mock
import pytest
from django.core.exceptions import ValidationError
from authentic2.validators import EmailValidator, validate_password
from authentic2.validators import EmailValidator, HexaColourValidator, validate_password
def test_validate_password():
@ -34,6 +34,17 @@ def test_validate_password():
validate_password('000aaaaZZZZ')
def test_validate_colour():
validator = HexaColourValidator()
with pytest.raises(ValidationError):
validator('abc')
with pytest.raises(ValidationError):
validator('blue')
with pytest.raises(ValidationError):
validator('#green')
validator('#ff00ff')
def test_digits_password_policy(settings):
settings.A2_PASSWORD_POLICY_REGEX = '^[0-9]{8}$'
settings.A2_PASSWORD_POLICY_REGEX_ERROR_MSG = 'pasbon'