Projet

Général

Profil

0001-misc-add-logo-and-text-color-for-service-and-OU.patch

Voir les différences:

Subject: [PATCH 1/2] misc: add logo and text color for service and OU

 .../migrations/0027_auto_20220331_1521.py     | 31 +++++++++++++++++++
 src/authentic2/a2_rbac/models.py              |  9 ++++++
 src/authentic2/manager/forms.py               |  3 ++
 .../migrations/0037_auto_20220331_1513.py     | 31 +++++++++++++++++++
 src/authentic2/models.py                      | 12 +++++++
 src/authentic2/validators.py                  |  9 ++++++
 tests/idp_oidc/test_misc.py                   |  4 +++
 tests/test_validators.py                      | 13 +++++++-
 8 files changed, 111 insertions(+), 1 deletion(-)
 create mode 100644 src/authentic2/a2_rbac/migrations/0027_auto_20220331_1521.py
 create mode 100644 src/authentic2/migrations/0037_auto_20220331_1513.py
src/authentic2/a2_rbac/migrations/0027_auto_20220331_1521.py
1
# Generated by Django 2.2.24 on 2022-03-31 13:21
2

  
3
from django.db import migrations, models
4

  
5
import authentic2.validators
6

  
7

  
8
class Migration(migrations.Migration):
9

  
10
    dependencies = [
11
        ('a2_rbac', '0026_add_roleparenting_soft_delete'),
12
    ]
13

  
14
    operations = [
15
        migrations.AddField(
16
            model_name='organizationalunit',
17
            name='colour',
18
            field=models.CharField(
19
                blank=True,
20
                max_length=32,
21
                null=True,
22
                validators=[authentic2.validators.HexaColourValidator()],
23
                verbose_name='Colour',
24
            ),
25
        ),
26
        migrations.AddField(
27
            model_name='organizationalunit',
28
            name='logo',
29
            field=models.ImageField(blank=True, upload_to='services/logos', verbose_name='Logo'),
30
        ),
31
    ]
src/authentic2/a2_rbac/models.py
14 14
# You should have received a copy of the GNU Affero General Public License
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17
import os
17 18
from collections import namedtuple
18 19

  
19 20
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
......
28 29

  
29 30
from authentic2.decorators import errorcollector
30 31
from authentic2.utils.cache import GlobalCache
32
from authentic2.validators import HexaColourValidator
31 33
from django_rbac import utils as rbac_utils
32 34
from django_rbac.models import (
33 35
    VIEW_OP,
......
113 115
        default=760,  # two years + 1 month
114 116
    )
115 117
    home_url = models.URLField(verbose_name=_('Home URL'), max_length=256, null=True, blank=True)
118
    logo = models.ImageField(verbose_name=_('Logo'), blank=True, upload_to='services/logos')
119
    colour = models.CharField(
120
        verbose_name=_('Colour'), null=True, blank=True, max_length=32, validators=[HexaColourValidator()]
121
    )
116 122

  
117 123
    objects = managers.OrganizationalUnitManager()
118 124

  
......
166 172
        )
167 173

  
168 174
    def delete(self, *args, **kwargs):
175
        if self.logo and os.path.exists(self.logo.path):
176
            os.unlink(self.logo.path)
177

  
169 178
        Permission.objects.filter(ou=self).delete()
170 179
        return super(OrganizationalUnitAbstractBase, self).delete(*args, **kwargs)
171 180

  
src/authentic2/manager/forms.py
633 633
    def __init__(self, *args, **kwargs):
634 634
        super().__init__(*args, **kwargs)
635 635
        self.fields['name'].label = _('label').title()
636
        self.fields['colour'].widget = forms.TextInput(attrs={'type': 'color'})
636 637

  
637 638
    class Meta:
638 639
        model = OrganizationalUnit
......
650 651
            'clean_unused_accounts_alert',
651 652
            'clean_unused_accounts_deletion',
652 653
            'home_url',
654
            'logo',
655
            'colour',
653 656
        )
654 657

  
655 658

  
src/authentic2/migrations/0037_auto_20220331_1513.py
1
# Generated by Django 2.2.24 on 2022-03-31 13:13
2

  
3
from django.db import migrations, models
4

  
5
import authentic2.validators
6

  
7

  
8
class Migration(migrations.Migration):
9

  
10
    dependencies = [
11
        ('authentic2', '0036_service_profile_types'),
12
    ]
13

  
14
    operations = [
15
        migrations.AddField(
16
            model_name='service',
17
            name='colour',
18
            field=models.CharField(
19
                blank=True,
20
                max_length=32,
21
                null=True,
22
                validators=[authentic2.validators.HexaColourValidator()],
23
                verbose_name='Colour',
24
            ),
25
        ),
26
        migrations.AddField(
27
            model_name='service',
28
            name='logo',
29
            field=models.ImageField(blank=True, upload_to='services/logos', verbose_name='Logo'),
30
        ),
31
    ]
src/authentic2/models.py
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17 17
import datetime
18
import os
18 19
import time
19 20
import urllib.parse
20 21
import uuid
......
37 38

  
38 39
from authentic2.a2_rbac.models import Role
39 40
from authentic2.utils.crypto import base64url_decode, base64url_encode
41
from authentic2.validators import HexaColourValidator
40 42

  
41 43
# install our natural_key implementation
42 44
from . import managers
......
382 384
        verbose_name=_('callback url when unauthorized'), max_length=256, null=True, blank=True
383 385
    )
384 386
    home_url = models.URLField(verbose_name=_('Home URL'), max_length=256, null=True, blank=True)
387
    logo = models.ImageField(verbose_name=_('Logo'), blank=True, upload_to='services/logos')
388
    colour = models.CharField(
389
        verbose_name=_('Colour'), null=True, blank=True, max_length=32, validators=[HexaColourValidator()]
390
    )
385 391

  
386 392
    profile_types = models.ManyToManyField(
387 393
        to='custom_user.ProfileType',
......
470 476
            urls.update(service.get_base_urls())
471 477
        return list(urls)
472 478

  
479
    def delete(self, *args, **kwargs):
480
        if self.logo and os.path.exists(self.logo.path):
481
            os.unlink(self.logo.path)
482

  
483
        return super().delete(*args, **kwargs)
484

  
473 485

  
474 486
Service._meta.natural_key = [['slug', 'ou']]
475 487

  
src/authentic2/validators.py
124 124

  
125 125
    def __eq__(self, other):
126 126
        return isinstance(other, self.__class__) and self.message == other.message and self.code == other.code
127

  
128

  
129
class HexaColourValidator(RegexValidator):
130
    """Validates that the string is a hexadecimal colour"""
131

  
132
    def __init__(self, *args, **kwargs):
133
        self.regex = '#[0-9a-fA-F]{6}'
134
        self.message = _('Hexadecimal value only allowed.')
135
        super().__init__(*args, **kwargs)
tests/idp_oidc/test_misc.py
85 85
        'authorization_flow': OIDCClient.FLOW_IMPLICIT,
86 86
        'idtoken_duration': datetime.timedelta(hours=1),
87 87
        'post_logout_redirect_uris': 'https://example.com/',
88
        'home_url': 'https://example.com/',
88 89
    },
89 90
    {
90 91
        'frontchannel_logout_uri': 'https://example.com/southpark/logout/',
......
92 93
    {
93 94
        'frontchannel_logout_uri': 'https://example.com/southpark/logout/',
94 95
        'frontchannel_timeout': 3000,
96
        'colour': '#ff00ff',
95 97
    },
96 98
    {
97 99
        'identifier_policy': OIDCClient.POLICY_PAIRWISE_REVERSIBLE,
......
120 122
    response.form.set('unauthorized_url', 'https://example.com/southpark/')
121 123
    response.form.set('redirect_uris', 'https://example.com/callbac%C3%A9')
122 124
    for key, value in other_attributes.items():
125
        if isinstance(value, datetime.timedelta):
126
            value = f'{value.total_seconds()}'
123 127
        response.form.set(key, value)
124 128
    response = response.form.submit().follow()
125 129
    assert OIDCClient.objects.count() == 1
tests/test_validators.py
21 21
import pytest
22 22
from django.core.exceptions import ValidationError
23 23

  
24
from authentic2.validators import EmailValidator, validate_password
24
from authentic2.validators import EmailValidator, HexaColourValidator, validate_password
25 25

  
26 26

  
27 27
def test_validate_password():
......
34 34
    validate_password('000aaaaZZZZ')
35 35

  
36 36

  
37
def test_validate_colour():
38
    validator = HexaColourValidator()
39
    with pytest.raises(ValidationError):
40
        validator('abc')
41
    with pytest.raises(ValidationError):
42
        validator('blue')
43
    with pytest.raises(ValidationError):
44
        validator('#green')
45
    validator('#ff00ff')
46

  
47

  
37 48
def test_digits_password_policy(settings):
38 49
    settings.A2_PASSWORD_POLICY_REGEX = '^[0-9]{8}$'
39 50
    settings.A2_PASSWORD_POLICY_REGEX_ERROR_MSG = 'pasbon'
40
-