misc: add logo and text color for service and OU (#47406)
This commit is contained in:
parent
5e772e6b1e
commit
10366ac244
|
@ -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",)}
|
||||
|
|
|
@ -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'),
|
||||
),
|
||||
]
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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',
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -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'),
|
||||
),
|
||||
]
|
|
@ -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']]
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
Loading…
Reference in New Issue