Merge branch 'aureplop/kfet-auth' into aureplop/kfet-auth_cms
+ Move migrations. + Update tests to use new url names and new permissions.
This commit is contained in:
commit
c524da22fe
25 changed files with 3039 additions and 112 deletions
|
@ -20,7 +20,6 @@ variables:
|
||||||
# psql password authentication
|
# psql password authentication
|
||||||
PGPASSWORD: $POSTGRES_PASSWORD
|
PGPASSWORD: $POSTGRES_PASSWORD
|
||||||
|
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
paths:
|
paths:
|
||||||
- vendor/python
|
- vendor/python
|
||||||
|
@ -31,12 +30,12 @@ before_script:
|
||||||
- mkdir -p vendor/{python,pip,apt}
|
- mkdir -p vendor/{python,pip,apt}
|
||||||
- apt-get update -q && apt-get -o dir::cache::archives="vendor/apt" install -yqq postgresql-client
|
- apt-get update -q && apt-get -o dir::cache::archives="vendor/apt" install -yqq postgresql-client
|
||||||
- sed -E 's/^REDIS_HOST.*/REDIS_HOST = "redis"/' cof/settings/secret_example.py > cof/settings/secret.py
|
- sed -E 's/^REDIS_HOST.*/REDIS_HOST = "redis"/' cof/settings/secret_example.py > cof/settings/secret.py
|
||||||
|
- sed -i.bak -E 's;^REDIS_PASSWD = .*$;REDIS_PASSWD = "";' cof/settings/secret.py
|
||||||
# Remove the old test database if it has not been done yet
|
# Remove the old test database if it has not been done yet
|
||||||
- psql --username=$POSTGRES_USER --host=$DBHOST -c "DROP DATABASE IF EXISTS test_$POSTGRES_DB"
|
- psql --username=$POSTGRES_USER --host=$DBHOST -c "DROP DATABASE IF EXISTS test_$POSTGRES_DB"
|
||||||
- pip install --cache-dir vendor/pip -t vendor/python -r requirements.txt
|
- pip install --upgrade --cache-dir vendor/pip -t vendor/python -r requirements.txt
|
||||||
- redis-cli config set requirepass $REDIS_PASSWD || true
|
|
||||||
|
|
||||||
test:
|
test:
|
||||||
stage: test
|
stage: test
|
||||||
script:
|
script:
|
||||||
- python manage.py test -v3
|
- python manage.py test
|
||||||
|
|
|
@ -5,17 +5,34 @@ from django.db import migrations, models
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
def forwards_func(apps, schema_editor):
|
|
||||||
|
def fill_tirage_fields(apps, schema_editor):
|
||||||
|
"""
|
||||||
|
Create a `Tirage` to fill new field `tirage` of `Participant`
|
||||||
|
and `Spectacle` already existing.
|
||||||
|
"""
|
||||||
|
Participant = apps.get_model("bda", "Participant")
|
||||||
|
Spectacle = apps.get_model("bda", "Spectacle")
|
||||||
Tirage = apps.get_model("bda", "Tirage")
|
Tirage = apps.get_model("bda", "Tirage")
|
||||||
db_alias = schema_editor.connection.alias
|
|
||||||
Tirage.objects.using(db_alias).bulk_create([
|
# These querysets only contains instances not linked to any `Tirage`.
|
||||||
Tirage(
|
participants = Participant.objects.filter(tirage=None)
|
||||||
id=1,
|
spectacles = Spectacle.objects.filter(tirage=None)
|
||||||
title="Tirage de test (migration)",
|
|
||||||
active=False,
|
if not participants.count() and not spectacles.count():
|
||||||
ouverture=timezone.now(),
|
# No need to create a "trash" tirage.
|
||||||
fermeture=timezone.now()),
|
return
|
||||||
])
|
|
||||||
|
tirage = Tirage.objects.create(
|
||||||
|
title="Tirage de test (migration)",
|
||||||
|
active=False,
|
||||||
|
ouverture=timezone.now(),
|
||||||
|
fermeture=timezone.now(),
|
||||||
|
)
|
||||||
|
|
||||||
|
participants.update(tirage=tirage)
|
||||||
|
spectacles.update(tirage=tirage)
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
@ -35,22 +52,33 @@ class Migration(migrations.Migration):
|
||||||
('active', models.BooleanField(default=True, verbose_name=b'Tirage actif')),
|
('active', models.BooleanField(default=True, verbose_name=b'Tirage actif')),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.RunPython(forwards_func, migrations.RunPython.noop),
|
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='participant',
|
model_name='participant',
|
||||||
name='user',
|
name='user',
|
||||||
field=models.ForeignKey(to=settings.AUTH_USER_MODEL),
|
field=models.ForeignKey(to=settings.AUTH_USER_MODEL),
|
||||||
),
|
),
|
||||||
|
# Create fields `spectacle` for `Participant` and `Spectacle` models.
|
||||||
|
# These fields are not nullable, but we first create them as nullable
|
||||||
|
# to give a default value for existing instances of these models.
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='participant',
|
model_name='participant',
|
||||||
name='tirage',
|
name='tirage',
|
||||||
field=models.ForeignKey(default=1, to='bda.Tirage'),
|
field=models.ForeignKey(to='bda.Tirage', null=True),
|
||||||
preserve_default=False,
|
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='spectacle',
|
model_name='spectacle',
|
||||||
name='tirage',
|
name='tirage',
|
||||||
field=models.ForeignKey(default=1, to='bda.Tirage'),
|
field=models.ForeignKey(to='bda.Tirage', null=True),
|
||||||
preserve_default=False,
|
),
|
||||||
|
migrations.RunPython(fill_tirage_fields, migrations.RunPython.noop),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='participant',
|
||||||
|
name='tirage',
|
||||||
|
field=models.ForeignKey(to='bda.Tirage'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='spectacle',
|
||||||
|
name='tirage',
|
||||||
|
field=models.ForeignKey(to='bda.Tirage'),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -782,9 +782,9 @@ def catalogue(request, request_type):
|
||||||
.select_related('location')
|
.select_related('location')
|
||||||
.prefetch_related('quote_set')
|
.prefetch_related('quote_set')
|
||||||
)
|
)
|
||||||
if categories_id:
|
if categories_id and 0 not in categories_id:
|
||||||
shows_qs = shows_qs.filter(category__id__in=categories_id)
|
shows_qs = shows_qs.filter(category__id__in=categories_id)
|
||||||
if locations_id:
|
if locations_id and 0 not in locations_id:
|
||||||
shows_qs = shows_qs.filter(location__id__in=locations_id)
|
shows_qs = shows_qs.filter(location__id__in=locations_id)
|
||||||
|
|
||||||
# On convertit les descriptions à envoyer en une liste facilement
|
# On convertit les descriptions à envoyer en une liste facilement
|
||||||
|
|
|
@ -45,6 +45,7 @@ RECAPTCHA_PUBLIC_KEY = import_secret("RECAPTCHA_PUBLIC_KEY")
|
||||||
RECAPTCHA_PRIVATE_KEY = import_secret("RECAPTCHA_PRIVATE_KEY")
|
RECAPTCHA_PRIVATE_KEY = import_secret("RECAPTCHA_PRIVATE_KEY")
|
||||||
|
|
||||||
KFETOPEN_TOKEN = import_secret("KFETOPEN_TOKEN")
|
KFETOPEN_TOKEN = import_secret("KFETOPEN_TOKEN")
|
||||||
|
LDAP_SERVER_URL = import_secret("LDAP_SERVER_URL")
|
||||||
|
|
||||||
|
|
||||||
BASE_DIR = os.path.dirname(
|
BASE_DIR = os.path.dirname(
|
||||||
|
|
|
@ -58,7 +58,7 @@ def autocomplete(request):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Fetching data from the SPI
|
# Fetching data from the SPI
|
||||||
if hasattr(settings, 'LDAP_SERVER_URL'):
|
if getattr(settings, 'LDAP_SERVER_URL', None):
|
||||||
# Fetching
|
# Fetching
|
||||||
ldap_query = '(&{:s})'.format(''.join(
|
ldap_query = '(&{:s})'.format(''.join(
|
||||||
'(|(cn=*{bit:s}*)(uid=*{bit:s}*))'.format(bit=bit)
|
'(|(cn=*{bit:s}*)(uid=*{bit:s}*))'.format(bit=bit)
|
||||||
|
|
|
@ -577,7 +577,7 @@ def export_members(request):
|
||||||
writer = unicodecsv.writer(response)
|
writer = unicodecsv.writer(response)
|
||||||
for profile in CofProfile.objects.filter(is_cof=True).all():
|
for profile in CofProfile.objects.filter(is_cof=True).all():
|
||||||
user = profile.user
|
user = profile.user
|
||||||
bits = [profile.id, user.username, user.first_name, user.last_name,
|
bits = [user.id, user.username, user.first_name, user.last_name,
|
||||||
user.email, profile.phone, profile.occupation,
|
user.email, profile.phone, profile.occupation,
|
||||||
profile.departement, profile.type_cotiz]
|
profile.departement, profile.type_cotiz]
|
||||||
writer.writerow([str(bit) for bit in bits])
|
writer.writerow([str(bit) for bit in bits])
|
||||||
|
@ -596,7 +596,7 @@ def csv_export_mega(filename, qs):
|
||||||
comments = "---".join(
|
comments = "---".join(
|
||||||
[comment.content for comment in reg.comments.all()])
|
[comment.content for comment in reg.comments.all()])
|
||||||
bits = [user.username, user.first_name, user.last_name, user.email,
|
bits = [user.username, user.first_name, user.last_name, user.email,
|
||||||
profile.phone, profile.id,
|
profile.phone, user.id,
|
||||||
profile.comments if profile.comments else "", comments]
|
profile.comments if profile.comments else "", comments]
|
||||||
|
|
||||||
writer.writerow([str(bit) for bit in bits])
|
writer.writerow([str(bit) for bit in bits])
|
||||||
|
|
|
@ -14,7 +14,7 @@ from kfet.models import Account
|
||||||
from . import KFET_GENERIC_TRIGRAMME, KFET_GENERIC_USERNAME
|
from . import KFET_GENERIC_TRIGRAMME, KFET_GENERIC_USERNAME
|
||||||
from .backends import AccountBackend, GenericBackend
|
from .backends import AccountBackend, GenericBackend
|
||||||
from .fields import GroupsField, CorePermissionsField
|
from .fields import GroupsField, CorePermissionsField
|
||||||
from .forms import GroupForm, UserGroupForm
|
from .forms import UserGroupForm
|
||||||
from .middleware import TemporaryAuthMiddleware
|
from .middleware import TemporaryAuthMiddleware
|
||||||
from .models import GenericTeamToken, Group, Permission
|
from .models import GenericTeamToken, Group, Permission
|
||||||
from .utils import get_kfet_generic_user
|
from .utils import get_kfet_generic_user
|
||||||
|
@ -85,6 +85,7 @@ class GroupFormTests(TestCase):
|
||||||
content_type=ot_ct, codename='cool')
|
content_type=ot_ct, codename='cool')
|
||||||
|
|
||||||
def test_creation(self):
|
def test_creation(self):
|
||||||
|
from .forms import GroupForm
|
||||||
data = {
|
data = {
|
||||||
'name': 'Another Group',
|
'name': 'Another Group',
|
||||||
'permissions': [self.kf_perm1.pk],
|
'permissions': [self.kf_perm1.pk],
|
||||||
|
@ -101,6 +102,7 @@ class GroupFormTests(TestCase):
|
||||||
Non-kfet permissions of Group are kept when the form is submitted.
|
Non-kfet permissions of Group are kept when the form is submitted.
|
||||||
Regression test for #168.
|
Regression test for #168.
|
||||||
"""
|
"""
|
||||||
|
from .forms import GroupForm
|
||||||
self.kf_group.permissions.add(self.ot_perm)
|
self.kf_group.permissions.add(self.ot_perm)
|
||||||
|
|
||||||
selected = [self.kf_perm1, self.kf_perm2]
|
selected = [self.kf_perm1, self.kf_perm2]
|
||||||
|
|
|
@ -76,7 +76,7 @@ def account_create(request):
|
||||||
queries['users_notcof'].values_list('username', flat=True))
|
queries['users_notcof'].values_list('username', flat=True))
|
||||||
|
|
||||||
# Fetching data from the SPI
|
# Fetching data from the SPI
|
||||||
if hasattr(settings, 'LDAP_SERVER_URL'):
|
if getattr(settings, 'LDAP_SERVER_URL', None):
|
||||||
# Fetching
|
# Fetching
|
||||||
ldap_query = '(&{:s})'.format(''.join(
|
ldap_query = '(&{:s})'.format(''.join(
|
||||||
'(|(cn=*{bit:s}*)(uid=*{bit:s}*))'.format(bit=word)
|
'(|(cn=*{bit:s}*)(uid=*{bit:s}*))'.format(bit=word)
|
||||||
|
@ -106,6 +106,7 @@ def account_create(request):
|
||||||
return render(request, "kfet/account_create_autocomplete.html", data)
|
return render(request, "kfet/account_create_autocomplete.html", data)
|
||||||
|
|
||||||
|
|
||||||
|
@teamkfet_required
|
||||||
def account_search(request):
|
def account_search(request):
|
||||||
if "q" not in request.GET:
|
if "q" not in request.GET:
|
||||||
raise Http404
|
raise Http404
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
"kfetpage"
|
"kfetpage"
|
||||||
],
|
],
|
||||||
"owner": [
|
"owner": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"expired": false,
|
"expired": false,
|
||||||
"first_published_at": "2017-05-28T04:20:00.000Z",
|
"first_published_at": "2017-05-28T04:20:00.000Z",
|
||||||
|
@ -83,7 +83,7 @@
|
||||||
"kfetpage"
|
"kfetpage"
|
||||||
],
|
],
|
||||||
"owner": [
|
"owner": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"expired": false,
|
"expired": false,
|
||||||
"first_published_at": "2017-05-28T04:20:00.000Z",
|
"first_published_at": "2017-05-28T04:20:00.000Z",
|
||||||
|
@ -113,7 +113,7 @@
|
||||||
"kfetpage"
|
"kfetpage"
|
||||||
],
|
],
|
||||||
"owner": [
|
"owner": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"expired": false,
|
"expired": false,
|
||||||
"first_published_at": "2017-05-28T04:20:00.000Z",
|
"first_published_at": "2017-05-28T04:20:00.000Z",
|
||||||
|
@ -143,7 +143,7 @@
|
||||||
"kfetpage"
|
"kfetpage"
|
||||||
],
|
],
|
||||||
"owner": [
|
"owner": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"expired": false,
|
"expired": false,
|
||||||
"first_published_at": "2017-05-28T04:20:00.000Z",
|
"first_published_at": "2017-05-28T04:20:00.000Z",
|
||||||
|
@ -173,7 +173,7 @@
|
||||||
"kfetpage"
|
"kfetpage"
|
||||||
],
|
],
|
||||||
"owner": [
|
"owner": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"expired": false,
|
"expired": false,
|
||||||
"first_published_at": "2017-05-28T04:20:00.000Z",
|
"first_published_at": "2017-05-28T04:20:00.000Z",
|
||||||
|
@ -203,7 +203,7 @@
|
||||||
"kfetpage"
|
"kfetpage"
|
||||||
],
|
],
|
||||||
"owner": [
|
"owner": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"expired": false,
|
"expired": false,
|
||||||
"first_published_at": "2017-05-28T04:20:00.000Z",
|
"first_published_at": "2017-05-28T04:20:00.000Z",
|
||||||
|
@ -233,7 +233,7 @@
|
||||||
"page"
|
"page"
|
||||||
],
|
],
|
||||||
"owner": [
|
"owner": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"expired": false,
|
"expired": false,
|
||||||
"first_published_at": "2017-05-28T04:20:00.000Z",
|
"first_published_at": "2017-05-28T04:20:00.000Z",
|
||||||
|
@ -263,7 +263,7 @@
|
||||||
"kfetpage"
|
"kfetpage"
|
||||||
],
|
],
|
||||||
"owner": [
|
"owner": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"expired": false,
|
"expired": false,
|
||||||
"first_published_at": "2017-05-28T04:20:00.000Z",
|
"first_published_at": "2017-05-28T04:20:00.000Z",
|
||||||
|
@ -681,7 +681,7 @@
|
||||||
"fields": {
|
"fields": {
|
||||||
"created_at": "2017-05-30T04:20:00.000Z",
|
"created_at": "2017-05-30T04:20:00.000Z",
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"title": "K-F\u00eat - Plan d'acc\u00e8s",
|
"title": "K-F\u00eat - Plan d'acc\u00e8s",
|
||||||
|
@ -694,7 +694,7 @@
|
||||||
"fields": {
|
"fields": {
|
||||||
"created_at": "2017-05-30T04:20:00.000Z",
|
"created_at": "2017-05-30T04:20:00.000Z",
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"title": "K-F\u00eat - Demande d'autorisation",
|
"title": "K-F\u00eat - Demande d'autorisation",
|
||||||
|
@ -707,7 +707,7 @@
|
||||||
"fields": {
|
"fields": {
|
||||||
"created_at": "2017-05-30T04:20:00.000Z",
|
"created_at": "2017-05-30T04:20:00.000Z",
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"title": "K-F\u00eat - Trait\u00e9 de Flipper Th\u00e9orique",
|
"title": "K-F\u00eat - Trait\u00e9 de Flipper Th\u00e9orique",
|
||||||
|
@ -730,7 +730,7 @@
|
||||||
"title": "K-F\u00eat - Amazon Hunt",
|
"title": "K-F\u00eat - Amazon Hunt",
|
||||||
"width": 200,
|
"width": 200,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -750,7 +750,7 @@
|
||||||
"title": "K-F\u00eat - Fun Machine",
|
"title": "K-F\u00eat - Fun Machine",
|
||||||
"width": 200,
|
"width": 200,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -767,7 +767,7 @@
|
||||||
"title": "Hugo Manet",
|
"title": "Hugo Manet",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -787,7 +787,7 @@
|
||||||
"title": "Lisa Gourdon",
|
"title": "Lisa Gourdon",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -807,7 +807,7 @@
|
||||||
"title": "Pierre Quesselaire",
|
"title": "Pierre Quesselaire",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -827,7 +827,7 @@
|
||||||
"title": "Thibault Scoquard",
|
"title": "Thibault Scoquard",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -847,7 +847,7 @@
|
||||||
"title": "Arnaud Fanthomme",
|
"title": "Arnaud Fanthomme",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -867,7 +867,7 @@
|
||||||
"title": "Vincent Balerdi",
|
"title": "Vincent Balerdi",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -887,7 +887,7 @@
|
||||||
"title": "Nathana\u00ebl Willaime",
|
"title": "Nathana\u00ebl Willaime",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -907,7 +907,7 @@
|
||||||
"title": "\u00c9lisabeth Miller",
|
"title": "\u00c9lisabeth Miller",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -927,7 +927,7 @@
|
||||||
"title": "Arthur Lesage",
|
"title": "Arthur Lesage",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -947,7 +947,7 @@
|
||||||
"title": "Sarah Asset",
|
"title": "Sarah Asset",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -967,7 +967,7 @@
|
||||||
"title": "Alexandre Legrand",
|
"title": "Alexandre Legrand",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -987,7 +987,7 @@
|
||||||
"title": "\u00c9tienne Baudel",
|
"title": "\u00c9tienne Baudel",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -1007,7 +1007,7 @@
|
||||||
"title": "Marine Snape",
|
"title": "Marine Snape",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -1027,7 +1027,7 @@
|
||||||
"title": "Anatole Gosset",
|
"title": "Anatole Gosset",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -1047,7 +1047,7 @@
|
||||||
"title": "Jacko Rastikian",
|
"title": "Jacko Rastikian",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -1067,7 +1067,7 @@
|
||||||
"title": "Alexandre Jannaud",
|
"title": "Alexandre Jannaud",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -1087,7 +1087,7 @@
|
||||||
"title": "Aur\u00e9lien Delobelle",
|
"title": "Aur\u00e9lien Delobelle",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -1107,7 +1107,7 @@
|
||||||
"title": "Sylvain Douteau",
|
"title": "Sylvain Douteau",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -1127,7 +1127,7 @@
|
||||||
"title": "Rapha\u00ebl Lescanne",
|
"title": "Rapha\u00ebl Lescanne",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -1147,7 +1147,7 @@
|
||||||
"title": "Romain Gourvil",
|
"title": "Romain Gourvil",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -1167,7 +1167,7 @@
|
||||||
"title": "Marie Labeye",
|
"title": "Marie Labeye",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -1187,7 +1187,7 @@
|
||||||
"title": "Oscar Blumberg",
|
"title": "Oscar Blumberg",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -1207,7 +1207,7 @@
|
||||||
"title": "Za\u00efd Allybokus",
|
"title": "Za\u00efd Allybokus",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -1227,7 +1227,7 @@
|
||||||
"title": "Damien Garreau",
|
"title": "Damien Garreau",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -1247,7 +1247,7 @@
|
||||||
"title": "Andr\u00e9a Londono-Lopez",
|
"title": "Andr\u00e9a Londono-Lopez",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -1267,7 +1267,7 @@
|
||||||
"title": "Tristan Roussel",
|
"title": "Tristan Roussel",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -1287,7 +1287,7 @@
|
||||||
"title": "Guillaume Vernade",
|
"title": "Guillaume Vernade",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -1307,7 +1307,7 @@
|
||||||
"title": "Lucas Mercier",
|
"title": "Lucas Mercier",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -1327,7 +1327,7 @@
|
||||||
"title": "Fran\u00e7ois Maillot",
|
"title": "Fran\u00e7ois Maillot",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
@ -1347,7 +1347,7 @@
|
||||||
"title": "Fabrice Catoire",
|
"title": "Fabrice Catoire",
|
||||||
"collection": 2,
|
"collection": 2,
|
||||||
"uploaded_by_user": [
|
"uploaded_by_user": [
|
||||||
"root"
|
"kfet_genericteam"
|
||||||
],
|
],
|
||||||
"created_at": "2017-05-30T04:20:00.000Z"
|
"created_at": "2017-05-30T04:20:00.000Z"
|
||||||
},
|
},
|
||||||
|
|
|
@ -123,7 +123,6 @@ class UserRestrictTeamForm(UserForm):
|
||||||
fields = ['first_name', 'last_name', 'email']
|
fields = ['first_name', 'last_name', 'email']
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AccountNegativeForm(forms.ModelForm):
|
class AccountNegativeForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = AccountNegative
|
model = AccountNegative
|
||||||
|
|
18
kfet/migrations/0057_add_perms_config.py
Normal file
18
kfet/migrations/0057_add_perms_config.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('kfet', '0056_change_account_meta'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='account',
|
||||||
|
options={'permissions': (('is_team', 'Is part of the team'), ('manage_perms', 'Gérer les permissions K-Fêt'), ('manage_addcosts', 'Gérer les majorations'), ('edit_balance_account', "Modifier la balance d'un compte"), ('change_account_password', "Modifier le mot de passe d'une personne de l'équipe"), ('special_add_account', 'Créer un compte avec une balance initiale'), ('can_force_close', 'Fermer manuellement la K-Fêt'), ('see_config', 'Voir la configuration K-Fêt'), ('change_config', 'Modifier la configuration K-Fêt'))},
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,12 +1,13 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
('kfet', '0057_add_perms_config'),
|
||||||
('kfet', '0056_change_account_meta'),
|
('kfet', '0056_change_account_meta'),
|
||||||
('kfet', '0054_update_promos'),
|
('kfet', '0054_update_promos'),
|
||||||
]
|
]
|
||||||
|
|
39
kfet/migrations/0060_amend_supplier.py
Normal file
39
kfet/migrations/0060_amend_supplier.py
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('kfet', '0059_create_generic'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='supplier',
|
||||||
|
name='address',
|
||||||
|
field=models.TextField(verbose_name='adresse', blank=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='supplier',
|
||||||
|
name='articles',
|
||||||
|
field=models.ManyToManyField(verbose_name='articles vendus', through='kfet.SupplierArticle', related_name='suppliers', to='kfet.Article'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='supplier',
|
||||||
|
name='comment',
|
||||||
|
field=models.TextField(verbose_name='commentaire', blank=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='supplier',
|
||||||
|
name='email',
|
||||||
|
field=models.EmailField(max_length=254, verbose_name='adresse mail', blank=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='supplier',
|
||||||
|
name='phone',
|
||||||
|
field=models.CharField(max_length=20, verbose_name='téléphone', blank=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -7,7 +7,7 @@ from django.db import migrations, models
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('kfet', '0059_create_generic'),
|
('kfet', '0060_amend_supplier'),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
|
@ -89,7 +89,7 @@ class Migration(migrations.Migration):
|
||||||
Data migration which performs permissions cleaning.
|
Data migration which performs permissions cleaning.
|
||||||
"""
|
"""
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('kfet', '0060_change_models_opts'),
|
('kfet', '0061_change_models_opts'),
|
||||||
('auth', '0006_require_contenttypes_0002'),
|
('auth', '0006_require_contenttypes_0002'),
|
||||||
('contenttypes', '0002_remove_content_type_name'),
|
('contenttypes', '0002_remove_content_type_name'),
|
||||||
]
|
]
|
|
@ -96,6 +96,8 @@ class Account(models.Model):
|
||||||
('special_add_account',
|
('special_add_account',
|
||||||
"Créer un compte avec une balance initiale"),
|
"Créer un compte avec une balance initiale"),
|
||||||
('can_force_close', "Fermer manuellement la K-Fêt"),
|
('can_force_close', "Fermer manuellement la K-Fêt"),
|
||||||
|
('see_config', "Voir la configuration K-Fêt"),
|
||||||
|
('change_config', "Modifier la configuration K-Fêt"),
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -581,17 +583,19 @@ class InventoryArticle(models.Model):
|
||||||
self.stock_error = self.stock_new - self.stock_old
|
self.stock_error = self.stock_new - self.stock_old
|
||||||
super(InventoryArticle, self).save(*args, **kwargs)
|
super(InventoryArticle, self).save(*args, **kwargs)
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class Supplier(models.Model):
|
class Supplier(models.Model):
|
||||||
articles = models.ManyToManyField(
|
articles = models.ManyToManyField(
|
||||||
Article,
|
Article,
|
||||||
through = 'SupplierArticle',
|
verbose_name=_("articles vendus"),
|
||||||
related_name = "suppliers")
|
through='SupplierArticle',
|
||||||
name = models.CharField("nom", max_length = 45)
|
related_name='suppliers',
|
||||||
address = models.TextField("adresse")
|
)
|
||||||
email = models.EmailField("adresse mail")
|
name = models.CharField(_("nom"), max_length=45)
|
||||||
phone = models.CharField("téléphone", max_length = 10)
|
address = models.TextField(_("adresse"), blank=True)
|
||||||
comment = models.TextField("commentaire")
|
email = models.EmailField(_("adresse mail"), blank=True)
|
||||||
|
phone = models.CharField(_("téléphone"), max_length=20, blank=True)
|
||||||
|
comment = models.TextField(_("commentaire"), blank=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Fournisseur")
|
verbose_name = _("Fournisseur")
|
||||||
|
@ -601,6 +605,7 @@ class Supplier(models.Model):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
class SupplierArticle(models.Model):
|
class SupplierArticle(models.Model):
|
||||||
supplier = models.ForeignKey(
|
supplier = models.ForeignKey(
|
||||||
Supplier, on_delete = models.PROTECT)
|
Supplier, on_delete = models.PROTECT)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import json
|
import json
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
from django.contrib.auth.models import AnonymousUser, Permission, User
|
from django.contrib.auth.models import AnonymousUser, Permission, User
|
||||||
from django.test import Client
|
from django.test import Client
|
||||||
|
@ -118,6 +119,11 @@ class OpenKfetViewsTest(ChannelTestCase):
|
||||||
"""OpenKfet views unit-tests suite."""
|
"""OpenKfet views unit-tests suite."""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
# Need this (and here) because of '<client>.login' in setUp
|
||||||
|
patcher_messages = mock.patch('gestioncof.signals.messages')
|
||||||
|
patcher_messages.start()
|
||||||
|
self.addCleanup(patcher_messages.stop)
|
||||||
|
|
||||||
# get some permissions
|
# get some permissions
|
||||||
perms = {
|
perms = {
|
||||||
'kfet.is_team': Permission.objects.get(codename='is_team'),
|
'kfet.is_team': Permission.objects.get(codename='is_team'),
|
||||||
|
@ -194,7 +200,8 @@ class OpenKfetConsumerTest(ChannelTestCase):
|
||||||
OpenKfetConsumer.group_send('kfet.open.team', {'test': 'plop'})
|
OpenKfetConsumer.group_send('kfet.open.team', {'test': 'plop'})
|
||||||
self.assertIsNone(c.receive())
|
self.assertIsNone(c.receive())
|
||||||
|
|
||||||
def test_team_user(self):
|
@mock.patch('gestioncof.signals.messages')
|
||||||
|
def test_team_user(self, mock_messages):
|
||||||
"""Team user is added to kfet.open.team group."""
|
"""Team user is added to kfet.open.team group."""
|
||||||
# setup team user and its client
|
# setup team user and its client
|
||||||
t = User.objects.create_user('team', '', 'team')
|
t = User.objects.create_user('team', '', 'team')
|
||||||
|
@ -224,6 +231,11 @@ class OpenKfetScenarioTest(ChannelTestCase):
|
||||||
"""OpenKfet functionnal tests suite."""
|
"""OpenKfet functionnal tests suite."""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
# Need this (and here) because of '<client>.login' in setUp
|
||||||
|
patcher_messages = mock.patch('gestioncof.signals.messages')
|
||||||
|
patcher_messages.start()
|
||||||
|
self.addCleanup(patcher_messages.stop)
|
||||||
|
|
||||||
# anonymous client (for views)
|
# anonymous client (for views)
|
||||||
self.c = Client()
|
self.c = Client()
|
||||||
# anonymous client (for websockets)
|
# anonymous client (for websockets)
|
||||||
|
|
95
kfet/tests/test_tests_utils.py
Normal file
95
kfet/tests/test_tests_utils.py
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.contrib.auth.models import Permission
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from gestioncof.models import CofProfile
|
||||||
|
|
||||||
|
from ..models import Account
|
||||||
|
from .testcases import TestCaseMixin
|
||||||
|
from .utils import (
|
||||||
|
create_user, create_team, create_root, get_perms, user_add_perms,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class UserHelpersTests(TestCaseMixin, TestCase):
|
||||||
|
|
||||||
|
def test_create_user(self):
|
||||||
|
"""create_user creates a basic user and its account."""
|
||||||
|
u = create_user()
|
||||||
|
a = u.profile.account_kfet
|
||||||
|
|
||||||
|
self.assertInstanceExpected(u, {
|
||||||
|
'get_full_name': 'first last',
|
||||||
|
'username': 'user',
|
||||||
|
})
|
||||||
|
self.assertFalse(u.user_permissions.exists())
|
||||||
|
|
||||||
|
self.assertEqual('000', a.trigramme)
|
||||||
|
|
||||||
|
def test_create_team(self):
|
||||||
|
u = create_team()
|
||||||
|
a = u.profile.account_kfet
|
||||||
|
|
||||||
|
self.assertInstanceExpected(u, {
|
||||||
|
'get_full_name': 'team member',
|
||||||
|
'username': 'team',
|
||||||
|
})
|
||||||
|
self.assertTrue(u.has_perm('kfet.is_team'))
|
||||||
|
|
||||||
|
self.assertEqual('100', a.trigramme)
|
||||||
|
|
||||||
|
def test_create_root(self):
|
||||||
|
u = create_root()
|
||||||
|
a = u.profile.account_kfet
|
||||||
|
|
||||||
|
self.assertInstanceExpected(u, {
|
||||||
|
'get_full_name': 'super user',
|
||||||
|
'username': 'root',
|
||||||
|
'is_superuser': True,
|
||||||
|
'is_staff': True,
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertEqual('200', a.trigramme)
|
||||||
|
|
||||||
|
|
||||||
|
class PermHelpersTest(TestCaseMixin, TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
cts = ContentType.objects.get_for_models(Account, CofProfile)
|
||||||
|
self.perm1 = Permission.objects.create(
|
||||||
|
content_type=cts[Account],
|
||||||
|
codename='test_perm',
|
||||||
|
name='Perm for test',
|
||||||
|
)
|
||||||
|
self.perm2 = Permission.objects.create(
|
||||||
|
content_type=cts[CofProfile],
|
||||||
|
codename='another_test_perm',
|
||||||
|
name='Another one',
|
||||||
|
)
|
||||||
|
self.perm_team = Permission.objects.get(
|
||||||
|
content_type__app_label='kfet',
|
||||||
|
codename='is_team',
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_get_perms(self):
|
||||||
|
perms = get_perms('kfet.test_perm', 'gestioncof.another_test_perm')
|
||||||
|
self.assertDictEqual(perms, {
|
||||||
|
'kfet.test_perm': self.perm1,
|
||||||
|
'gestioncof.another_test_perm': self.perm2,
|
||||||
|
})
|
||||||
|
|
||||||
|
def test_user_add_perms(self):
|
||||||
|
user = User.objects.create_user(username='user', password='user')
|
||||||
|
user.user_permissions.add(self.perm1)
|
||||||
|
|
||||||
|
user_add_perms(user, ['kfet.is_team', 'gestioncof.another_test_perm'])
|
||||||
|
|
||||||
|
self.assertQuerysetEqual(
|
||||||
|
user.user_permissions.all(),
|
||||||
|
map(repr, [self.perm1, self.perm2, self.perm_team]),
|
||||||
|
ordered=False,
|
||||||
|
)
|
File diff suppressed because it is too large
Load diff
353
kfet/tests/testcases.py
Normal file
353
kfet/tests/testcases.py
Normal file
|
@ -0,0 +1,353 @@
|
||||||
|
from unittest import mock
|
||||||
|
from urllib.parse import parse_qs, urlparse
|
||||||
|
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.http import QueryDict
|
||||||
|
from django.test import Client
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
|
from .utils import create_root, create_team, create_user
|
||||||
|
|
||||||
|
|
||||||
|
class TestCaseMixin:
|
||||||
|
"""Extends TestCase for kfet application tests."""
|
||||||
|
|
||||||
|
def assertForbidden(self, response):
|
||||||
|
"""
|
||||||
|
Test that the response (retrieved with a Client) is a denial of access.
|
||||||
|
|
||||||
|
The response should verify one of the following:
|
||||||
|
- its HTTP response code is 403,
|
||||||
|
- it redirects to the login page with a GET parameter named 'next'
|
||||||
|
whose value is the url of the requested page.
|
||||||
|
|
||||||
|
"""
|
||||||
|
request = response.wsgi_request
|
||||||
|
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
# Is this an HTTP Forbidden response ?
|
||||||
|
self.assertEqual(response.status_code, 403)
|
||||||
|
except AssertionError:
|
||||||
|
# A redirection to the login view is fine too.
|
||||||
|
|
||||||
|
# Let's build the login url with the 'next' param on current
|
||||||
|
# page.
|
||||||
|
full_path = request.get_full_path()
|
||||||
|
|
||||||
|
querystring = QueryDict(mutable=True)
|
||||||
|
querystring['next'] = full_path
|
||||||
|
|
||||||
|
login_url = '/login?' + querystring.urlencode(safe='/')
|
||||||
|
|
||||||
|
# We don't focus on what the login view does.
|
||||||
|
# So don't fetch the redirect.
|
||||||
|
self.assertRedirects(
|
||||||
|
response, login_url,
|
||||||
|
fetch_redirect_response=False,
|
||||||
|
)
|
||||||
|
except AssertionError:
|
||||||
|
raise AssertionError(
|
||||||
|
"%(http_method)s request at %(path)s should be forbidden for "
|
||||||
|
"%(username)s user.\n"
|
||||||
|
"Response isn't 403, nor a redirect to login view. Instead, "
|
||||||
|
"response code is %(code)d." % {
|
||||||
|
'http_method': request.method,
|
||||||
|
'path': request.get_full_path(),
|
||||||
|
'username': (
|
||||||
|
"'{}'".format(request.user)
|
||||||
|
if request.user.is_authenticated()
|
||||||
|
else 'anonymous'
|
||||||
|
),
|
||||||
|
'code': response.status_code,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def assertForbiddenKfet(self, response, form_ctx='form'):
|
||||||
|
"""
|
||||||
|
Test that a response (retrieved with a Client) contains error due to
|
||||||
|
lack of kfet permissions.
|
||||||
|
|
||||||
|
It checks that 'Permission refusée' is present in the non-field errors
|
||||||
|
of the form of response context at key 'form_ctx', or present in
|
||||||
|
messages.
|
||||||
|
|
||||||
|
This should be used for pages which can be accessed by the kfet team
|
||||||
|
members, but require additionnal permission(s) to make an operation.
|
||||||
|
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
try:
|
||||||
|
form = response.context[form_ctx]
|
||||||
|
self.assertIn("Permission refusée", form.non_field_errors())
|
||||||
|
except (AssertionError, AttributeError, KeyError):
|
||||||
|
messages = [str(msg) for msg in response.context['messages']]
|
||||||
|
self.assertIn("Permission refusée", messages)
|
||||||
|
except AssertionError:
|
||||||
|
request = response.wsgi_request
|
||||||
|
raise AssertionError(
|
||||||
|
"%(http_method)s request at %(path)s should raise an error "
|
||||||
|
"for %(username)s user.\n"
|
||||||
|
"Cannot find any errors in non-field errors of form "
|
||||||
|
"'%(form_ctx)s', nor in messages." % {
|
||||||
|
'http_method': request.method,
|
||||||
|
'path': request.get_full_path(),
|
||||||
|
'username': (
|
||||||
|
"'%s'" % request.user
|
||||||
|
if request.user.is_authenticated()
|
||||||
|
else 'anonymous'
|
||||||
|
),
|
||||||
|
'form_ctx': form_ctx,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def assertInstanceExpected(self, instance, expected):
|
||||||
|
"""
|
||||||
|
Test that the values of the attributes and without-argument methods of
|
||||||
|
'instance' are equal to 'expected' pairs.
|
||||||
|
"""
|
||||||
|
for attr, expected_value in expected.items():
|
||||||
|
value = getattr(instance, attr)
|
||||||
|
if callable(value):
|
||||||
|
value = value()
|
||||||
|
self.assertEqual(value, expected_value)
|
||||||
|
|
||||||
|
def assertUrlsEqual(self, actual, expected):
|
||||||
|
"""
|
||||||
|
Test that the url 'actual' is as 'expected'.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
actual (str): Url to verify.
|
||||||
|
expected: Two forms are accepted.
|
||||||
|
* (str): Expected url. Strings equality is checked.
|
||||||
|
* (dict): Its keys must be attributes of 'urlparse(actual)'.
|
||||||
|
Equality is checked for each present key, except for
|
||||||
|
'query' which must be a dict of the expected query string
|
||||||
|
parameters.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if type(expected) == dict:
|
||||||
|
parsed = urlparse(actual)
|
||||||
|
for part, expected_part in expected.items():
|
||||||
|
if part == 'query':
|
||||||
|
self.assertDictEqual(
|
||||||
|
parse_qs(parsed.query),
|
||||||
|
expected.get('query', {}),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.assertEqual(getattr(parsed, part), expected_part)
|
||||||
|
else:
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
|
||||||
|
class ViewTestCaseMixin(TestCaseMixin):
|
||||||
|
"""
|
||||||
|
TestCase extension to ease tests of kfet views.
|
||||||
|
|
||||||
|
|
||||||
|
Urls concerns
|
||||||
|
-------------
|
||||||
|
|
||||||
|
# Basic usage
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
url_name (str): Name of view under test, as given to 'reverse'
|
||||||
|
function.
|
||||||
|
url_args (list, optional): Will be given to 'reverse' call.
|
||||||
|
url_kwargs (dict, optional): Same.
|
||||||
|
url_expcted (str): What 'reverse' should return given previous
|
||||||
|
attributes.
|
||||||
|
|
||||||
|
View url can then be accessed at the 'url' attribute.
|
||||||
|
|
||||||
|
# Advanced usage
|
||||||
|
|
||||||
|
If multiple combinations of url name, args, kwargs can be used for a view,
|
||||||
|
it is possible to define 'urls_conf' attribute. It must be a list whose
|
||||||
|
each item is a dict defining arguments for 'reverse' call ('name', 'args',
|
||||||
|
'kwargs' keys) and its expected result ('expected' key).
|
||||||
|
|
||||||
|
The reversed urls can be accessed at the 't_urls' attribute.
|
||||||
|
|
||||||
|
|
||||||
|
Users concerns
|
||||||
|
--------------
|
||||||
|
|
||||||
|
During setup, three users are created with their kfet account:
|
||||||
|
- 'user': a basic user without any permission, account trigramme: 000,
|
||||||
|
- 'team': a user with kfet.is_team permission, account trigramme: 100,
|
||||||
|
- 'root': a superuser, account trigramme: 200.
|
||||||
|
Their password is their username.
|
||||||
|
|
||||||
|
One can create additionnal users with 'get_users_extra' method, or prevent
|
||||||
|
these 3 users to be created with 'get_users_base' method. See these two
|
||||||
|
methods for further informations.
|
||||||
|
|
||||||
|
By using 'register_user' method, these users can then be accessed at
|
||||||
|
'users' attribute by their label. Similarly, their kfet account is
|
||||||
|
registered on 'accounts' attribute.
|
||||||
|
|
||||||
|
A user label can be given to 'auth_user' attribute. The related user is
|
||||||
|
then authenticated on self.client during test setup. Its value defaults to
|
||||||
|
'None', meaning no user is authenticated.
|
||||||
|
|
||||||
|
|
||||||
|
Automated tests
|
||||||
|
---------------
|
||||||
|
|
||||||
|
# Url reverse
|
||||||
|
|
||||||
|
Based on url-related attributes/properties, the test 'test_urls' checks
|
||||||
|
that expected url is returned by 'reverse' (once with basic url usage and
|
||||||
|
each for advanced usage).
|
||||||
|
|
||||||
|
# Forbidden responses
|
||||||
|
|
||||||
|
The 'test_forbidden' test verifies that each user, from labels of
|
||||||
|
'auth_forbidden' attribute, can't access the url(s), i.e. response should
|
||||||
|
be a 403, or a redirect to login view.
|
||||||
|
|
||||||
|
Tested HTTP requests are given by 'http_methods' attribute. Additional data
|
||||||
|
can be given by defining an attribute '<method(lowercase)>_data'.
|
||||||
|
|
||||||
|
"""
|
||||||
|
url_name = None
|
||||||
|
url_expected = None
|
||||||
|
|
||||||
|
http_methods = ['GET']
|
||||||
|
|
||||||
|
auth_user = None
|
||||||
|
auth_forbidden = []
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
Warning: Do not forget to call super().setUp() in subclasses.
|
||||||
|
"""
|
||||||
|
# Signals handlers on login/logout send messages.
|
||||||
|
# Due to the way the Django' test Client performs login, this raise an
|
||||||
|
# error. As workaround, we mock the Django' messages module.
|
||||||
|
patcher_messages = mock.patch('gestioncof.signals.messages')
|
||||||
|
patcher_messages.start()
|
||||||
|
self.addCleanup(patcher_messages.stop)
|
||||||
|
|
||||||
|
# A test can mock 'django.utils.timezone.now' and give this as return
|
||||||
|
# value. E.g. it is useful if the test checks values of 'auto_now' or
|
||||||
|
# 'auto_now_add' fields.
|
||||||
|
self.now = timezone.now()
|
||||||
|
|
||||||
|
# These attributes register users and accounts instances.
|
||||||
|
self.users = {}
|
||||||
|
self.accounts = {}
|
||||||
|
|
||||||
|
for label, user in dict(self.users_base, **self.users_extra).items():
|
||||||
|
self.register_user(label, user)
|
||||||
|
|
||||||
|
if self.auth_user:
|
||||||
|
# The wrapper is a sanity check.
|
||||||
|
self.assertTrue(
|
||||||
|
self.client.login(
|
||||||
|
username=self.auth_user,
|
||||||
|
password=self.auth_user,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
del self.users_base
|
||||||
|
del self.users_extra
|
||||||
|
|
||||||
|
def get_users_base(self):
|
||||||
|
"""
|
||||||
|
Dict of <label: user instance>.
|
||||||
|
|
||||||
|
Note: Don't access yourself this property. Use 'users_base' attribute
|
||||||
|
which cache the returned value from here.
|
||||||
|
It allows to give functions calls, which creates users instances, as
|
||||||
|
values here.
|
||||||
|
|
||||||
|
"""
|
||||||
|
# Format desc: username, password, trigramme
|
||||||
|
return {
|
||||||
|
# user, user, 000
|
||||||
|
'user': create_user(),
|
||||||
|
# team, team, 100
|
||||||
|
'team': create_team(),
|
||||||
|
# root, root, 200
|
||||||
|
'root': create_root(),
|
||||||
|
}
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def users_base(self):
|
||||||
|
return self.get_users_base()
|
||||||
|
|
||||||
|
def get_users_extra(self):
|
||||||
|
"""
|
||||||
|
Dict of <label: user instance>.
|
||||||
|
|
||||||
|
Note: Don't access yourself this property. Use 'users_base' attribute
|
||||||
|
which cache the returned value from here.
|
||||||
|
It allows to give functions calls, which create users instances, as
|
||||||
|
values here.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return {}
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def users_extra(self):
|
||||||
|
return self.get_users_extra()
|
||||||
|
|
||||||
|
def register_user(self, label, user):
|
||||||
|
self.users[label] = user
|
||||||
|
if hasattr(user.profile, 'account_kfet'):
|
||||||
|
self.accounts[label] = user.profile.account_kfet
|
||||||
|
|
||||||
|
def get_user(self, label):
|
||||||
|
if self.auth_user is not None:
|
||||||
|
return self.auth_user
|
||||||
|
return self.auth_user_mapping.get(label)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def urls_conf(self):
|
||||||
|
return [{
|
||||||
|
'name': self.url_name,
|
||||||
|
'args': getattr(self, 'url_args', []),
|
||||||
|
'kwargs': getattr(self, 'url_kwargs', {}),
|
||||||
|
'expected': self.url_expected,
|
||||||
|
}]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def t_urls(self):
|
||||||
|
return [
|
||||||
|
reverse(
|
||||||
|
url_conf['name'],
|
||||||
|
args=url_conf.get('args', []),
|
||||||
|
kwargs=url_conf.get('kwargs', {}),
|
||||||
|
)
|
||||||
|
for url_conf in self.urls_conf]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def url(self):
|
||||||
|
return self.t_urls[0]
|
||||||
|
|
||||||
|
def test_urls(self):
|
||||||
|
for url, conf in zip(self.t_urls, self.urls_conf):
|
||||||
|
self.assertEqual(url, conf['expected'])
|
||||||
|
|
||||||
|
def test_forbidden(self):
|
||||||
|
for method in self.http_methods:
|
||||||
|
for user in self.auth_forbidden:
|
||||||
|
for url in self.t_urls:
|
||||||
|
self.check_forbidden(method, url, user)
|
||||||
|
|
||||||
|
def check_forbidden(self, method, url, user=None):
|
||||||
|
method = method.lower()
|
||||||
|
client = Client()
|
||||||
|
if user is not None:
|
||||||
|
client.login(username=user, password=user)
|
||||||
|
|
||||||
|
send_request = getattr(client, method)
|
||||||
|
data = getattr(self, '{}_data'.format(method), {})
|
||||||
|
|
||||||
|
r = send_request(url, data)
|
||||||
|
self.assertForbidden(r)
|
188
kfet/tests/utils.py
Normal file
188
kfet/tests/utils.py
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.contrib.auth.models import Permission
|
||||||
|
|
||||||
|
from ..models import Account
|
||||||
|
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
def _create_user_and_account(user_attrs, account_attrs, perms=None):
|
||||||
|
"""
|
||||||
|
Create a user and its account, and assign permissions to this user.
|
||||||
|
|
||||||
|
Arguments
|
||||||
|
user_attrs (dict): User data (first name, last name, password...).
|
||||||
|
account_attrs (dict): Account data (department, kfet password...).
|
||||||
|
perms (list of str: 'app.perm'): These permissions will be assigned to
|
||||||
|
the created user. No permission are assigned by default.
|
||||||
|
|
||||||
|
If 'password' is not given in 'user_attrs', username is used as password.
|
||||||
|
|
||||||
|
If 'kfet.is_team' is in 'perms' and 'password' is not in 'account_attrs',
|
||||||
|
the account password is 'kfetpwd_<user pwd>'.
|
||||||
|
|
||||||
|
"""
|
||||||
|
user_pwd = user_attrs.pop('password', user_attrs['username'])
|
||||||
|
user = User.objects.create(**user_attrs)
|
||||||
|
user.set_password(user_pwd)
|
||||||
|
user.save()
|
||||||
|
|
||||||
|
account_attrs['cofprofile'] = user.profile
|
||||||
|
kfet_pwd = account_attrs.pop('password', 'kfetpwd_{}'.format(user_pwd))
|
||||||
|
|
||||||
|
account = Account.objects.create(**account_attrs)
|
||||||
|
|
||||||
|
if perms is not None:
|
||||||
|
user = user_add_perms(user, perms)
|
||||||
|
|
||||||
|
if 'kfet.is_team' in perms:
|
||||||
|
account.change_pwd(kfet_pwd)
|
||||||
|
account.save()
|
||||||
|
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
def create_user(username='user', trigramme='000', **kwargs):
|
||||||
|
"""
|
||||||
|
Create a user without any permission and its kfet account.
|
||||||
|
|
||||||
|
username and trigramme are accepted as arguments (defaults to 'user' and
|
||||||
|
'000').
|
||||||
|
|
||||||
|
user_attrs, account_attrs and perms can be given as keyword arguments to
|
||||||
|
customize the user and its kfet account.
|
||||||
|
|
||||||
|
# Default values
|
||||||
|
|
||||||
|
User
|
||||||
|
* username: user
|
||||||
|
* password: user
|
||||||
|
* first_name: first
|
||||||
|
* last_name: last
|
||||||
|
* email: mail@user.net
|
||||||
|
Account
|
||||||
|
* trigramme: 000
|
||||||
|
|
||||||
|
"""
|
||||||
|
user_attrs = kwargs.setdefault('user_attrs', {})
|
||||||
|
|
||||||
|
user_attrs.setdefault('username', username)
|
||||||
|
user_attrs.setdefault('first_name', 'first')
|
||||||
|
user_attrs.setdefault('last_name', 'last')
|
||||||
|
user_attrs.setdefault('email', 'mail@user.net')
|
||||||
|
|
||||||
|
account_attrs = kwargs.setdefault('account_attrs', {})
|
||||||
|
account_attrs.setdefault('trigramme', trigramme)
|
||||||
|
|
||||||
|
return _create_user_and_account(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def create_team(username='team', trigramme='100', **kwargs):
|
||||||
|
"""
|
||||||
|
Create a user, member of the kfet team, and its kfet account.
|
||||||
|
|
||||||
|
username and trigramme are accepted as arguments (defaults to 'team' and
|
||||||
|
'100').
|
||||||
|
|
||||||
|
user_attrs, account_attrs and perms can be given as keyword arguments to
|
||||||
|
customize the user and its kfet account.
|
||||||
|
|
||||||
|
# Default values
|
||||||
|
|
||||||
|
User
|
||||||
|
* username: team
|
||||||
|
* password: team
|
||||||
|
* first_name: team
|
||||||
|
* last_name: member
|
||||||
|
* email: mail@team.net
|
||||||
|
Account
|
||||||
|
* trigramme: 100
|
||||||
|
* kfet password: kfetpwd_team
|
||||||
|
|
||||||
|
"""
|
||||||
|
user_attrs = kwargs.setdefault('user_attrs', {})
|
||||||
|
|
||||||
|
user_attrs.setdefault('username', username)
|
||||||
|
user_attrs.setdefault('first_name', 'team')
|
||||||
|
user_attrs.setdefault('last_name', 'member')
|
||||||
|
user_attrs.setdefault('email', 'mail@team.net')
|
||||||
|
|
||||||
|
account_attrs = kwargs.setdefault('account_attrs', {})
|
||||||
|
account_attrs.setdefault('trigramme', trigramme)
|
||||||
|
|
||||||
|
perms = kwargs.setdefault('perms', [])
|
||||||
|
perms.append('kfet.is_team')
|
||||||
|
|
||||||
|
return _create_user_and_account(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def create_root(username='root', trigramme='200', **kwargs):
|
||||||
|
"""
|
||||||
|
Create a superuser and its kfet account.
|
||||||
|
|
||||||
|
username and trigramme are accepted as arguments (defaults to 'root' and
|
||||||
|
'200').
|
||||||
|
|
||||||
|
user_attrs, account_attrs and perms can be given as keyword arguments to
|
||||||
|
customize the user and its kfet account.
|
||||||
|
|
||||||
|
# Default values
|
||||||
|
|
||||||
|
User
|
||||||
|
* username: root
|
||||||
|
* password: root
|
||||||
|
* first_name: super
|
||||||
|
* last_name: user
|
||||||
|
* email: mail@root.net
|
||||||
|
* is_staff, is_superuser: True
|
||||||
|
Account
|
||||||
|
* trigramme: 200
|
||||||
|
* kfet password: kfetpwd_root
|
||||||
|
|
||||||
|
"""
|
||||||
|
user_attrs = kwargs.setdefault('user_attrs', {})
|
||||||
|
|
||||||
|
user_attrs.setdefault('username', username)
|
||||||
|
user_attrs.setdefault('first_name', 'super')
|
||||||
|
user_attrs.setdefault('last_name', 'user')
|
||||||
|
user_attrs.setdefault('email', 'mail@root.net')
|
||||||
|
user_attrs['is_superuser'] = user_attrs['is_staff'] = True
|
||||||
|
|
||||||
|
account_attrs = kwargs.setdefault('account_attrs', {})
|
||||||
|
account_attrs.setdefault('trigramme', trigramme)
|
||||||
|
|
||||||
|
return _create_user_and_account(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def get_perms(*labels):
|
||||||
|
"""Return Permission instances from a list of '<app>.<perm_codename>'."""
|
||||||
|
perms = {}
|
||||||
|
for label in set(labels):
|
||||||
|
app_label, codename = label.split('.', 1)
|
||||||
|
perms[label] = Permission.objects.get(
|
||||||
|
content_type__app_label=app_label,
|
||||||
|
codename=codename,
|
||||||
|
)
|
||||||
|
return perms
|
||||||
|
|
||||||
|
|
||||||
|
def user_add_perms(user, perms_labels):
|
||||||
|
"""
|
||||||
|
Add perms to a user.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user (User instance)
|
||||||
|
perms (list of str 'app.perm_name')
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The same user (refetched from DB to avoid missing perms)
|
||||||
|
|
||||||
|
"""
|
||||||
|
perms = get_perms(*perms_labels)
|
||||||
|
user.user_permissions.add(*perms.values())
|
||||||
|
|
||||||
|
# If permissions have already been fetched for this user, we need to reload
|
||||||
|
# it to avoid using of the previous permissions cache.
|
||||||
|
# https://docs.djangoproject.com/en/1.11/topics/auth/default/#permission-caching
|
||||||
|
return User.objects.get(pk=user.pk)
|
|
@ -174,13 +174,9 @@ urlpatterns = [
|
||||||
# Settings urls
|
# Settings urls
|
||||||
# -----
|
# -----
|
||||||
|
|
||||||
url(r'^settings/$',
|
url(r'^settings/$', views.config_list,
|
||||||
permission_required('kfet.change_settings')
|
|
||||||
(views.SettingsList.as_view()),
|
|
||||||
name='kfet.settings'),
|
name='kfet.settings'),
|
||||||
url(r'^settings/edit$',
|
url(r'^settings/edit$', views.config_update,
|
||||||
permission_required('kfet.change_settings')
|
|
||||||
(views.SettingsUpdate.as_view()),
|
|
||||||
name='kfet.settings.update'),
|
name='kfet.settings.update'),
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ from django.views.generic.edit import CreateView, UpdateView
|
||||||
from django.core.urlresolvers import reverse, reverse_lazy
|
from django.core.urlresolvers import reverse, reverse_lazy
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.messages.views import SuccessMessageMixin
|
from django.contrib.messages.views import SuccessMessageMixin
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required, permission_required
|
||||||
from django.contrib.auth.models import User, Permission
|
from django.contrib.auth.models import User, Permission
|
||||||
from django.http import JsonResponse, Http404
|
from django.http import JsonResponse, Http404
|
||||||
from django.forms import formset_factory
|
from django.forms import formset_factory
|
||||||
|
@ -1399,6 +1399,9 @@ class SettingsList(TemplateView):
|
||||||
template_name = 'kfet/settings.html'
|
template_name = 'kfet/settings.html'
|
||||||
|
|
||||||
|
|
||||||
|
config_list = permission_required('kfet.see_config')(SettingsList.as_view())
|
||||||
|
|
||||||
|
|
||||||
class SettingsUpdate(SuccessMessageMixin, FormView):
|
class SettingsUpdate(SuccessMessageMixin, FormView):
|
||||||
form_class = KFetConfigForm
|
form_class = KFetConfigForm
|
||||||
template_name = 'kfet/settings_update.html'
|
template_name = 'kfet/settings_update.html'
|
||||||
|
@ -1407,13 +1410,17 @@ class SettingsUpdate(SuccessMessageMixin, FormView):
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
# Checking permission
|
# Checking permission
|
||||||
if not self.request.user.has_perm('kfet.change_settings'):
|
if not self.request.user.has_perm('kfet.change_config'):
|
||||||
form.add_error(None, 'Permission refusée')
|
form.add_error(None, 'Permission refusée')
|
||||||
return self.form_invalid(form)
|
return self.form_invalid(form)
|
||||||
form.save()
|
form.save()
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
config_update = (
|
||||||
|
permission_required('kfet.change_config')(SettingsUpdate.as_view())
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# -----
|
# -----
|
||||||
# Transfer views
|
# Transfer views
|
||||||
|
|
0
manage.py
Normal file → Executable file
0
manage.py
Normal file → Executable file
|
@ -26,6 +26,9 @@ python-dateutil
|
||||||
wagtail==1.10.*
|
wagtail==1.10.*
|
||||||
wagtailmenus==2.2.*
|
wagtailmenus==2.2.*
|
||||||
|
|
||||||
|
# Remove this when we switch to Django 1.11
|
||||||
|
djangorestframework==3.6.4
|
||||||
|
|
||||||
# This fork enables restore of forms.
|
# This fork enables restore of forms.
|
||||||
# Original project: https://bitbucket.org/tim_heap/django-formset-js
|
# Original project: https://bitbucket.org/tim_heap/django-formset-js
|
||||||
git+https://bitbucket.org/georgema1982/django-formset-js.git#egg=django-formset-js
|
git+https://bitbucket.org/georgema1982/django-formset-js.git#egg=django-formset-js
|
||||||
|
|
Loading…
Reference in a new issue