1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2025-07-06 23:44:01 +02:00

Compare commits

..

1 Commits

Author SHA1 Message Date
1c5e951c2f Merge branch 'beta' into 'master'
Various fixes

See merge request bde/nk20!133
2020-10-07 12:06:48 +02:00
19 changed files with 57 additions and 287 deletions

View File

@ -1,50 +0,0 @@
import sys
from django.db import migrations
def give_note_account_permissions(apps, schema_editor):
"""
Automatically manage the membership of the Note account.
"""
User = apps.get_model("auth", "user")
Membership = apps.get_model("member", "membership")
Role = apps.get_model("permission", "role")
note = User.objects.filter(username="note")
if not note.exists():
# We are in a test environment, don't log error message
if len(sys.argv) > 1 and sys.argv[1] == 'test':
return
print("Warning: Note account was not found. The note account was not imported.")
print("Make sure you have imported the NK15 database. The new import script handles correctly the permissions.")
print("This migration will be ignored, you can re-run it if you forgot the note account or ignore it if you "
"don't want this account.")
return
note = note.get()
# Set for the two clubs a large expiration date and the correct role.
for m in Membership.objects.filter(user_id=note.id).all():
m.date_end = "3142-12-12"
m.roles.set(Role.objects.filter(name="PC Kfet").all())
m.save()
# By default, the note account is only authorized to be logged from localhost.
note.password = "ipbased$127.0.0.1"
note.is_active = True
note.save()
# Ensure that the note of the account is disabled
note.note.inactivity_reason = 'forced'
note.note.is_active = False
note.save()
class Migration(migrations.Migration):
dependencies = [
('member', '0005_remove_null_tag_on_charfields'),
('permission', '0001_initial'),
]
operations = [
migrations.RunPython(give_note_account_permissions),
]

View File

@ -43,24 +43,8 @@ class UserTable(tables.Table):
section = tables.Column(accessor='profile__section')
# Override the column to let replace the URL
email = tables.EmailColumn(linkify=lambda record: "mailto:{}".format(record.email))
balance = tables.Column(accessor='note__balance', verbose_name=_("Balance"))
def render_email(self, record, value):
# Replace the email by a dash if the user can't see the profile detail
# Replace also the URL
if not PermissionBackend.check_perm(get_current_authenticated_user(), "member.view_profile", record.profile):
value = ""
record.email = value
return value
def render_section(self, record, value):
return value \
if PermissionBackend.check_perm(get_current_authenticated_user(), "member.view_profile", record.profile) \
else ""
def render_balance(self, record, value):
return pretty_money(value)\
if PermissionBackend.check_perm(get_current_authenticated_user(), "note.view_note", record.note) else ""

View File

@ -25,27 +25,25 @@
</a>
</dd>
{% if "member.view_profile"|has_perm:user_object.profile %}
<dt class="col-xl-6">{% trans 'section'|capfirst %}</dt>
<dd class="col-xl-6">{{ user_object.profile.section }}</dd>
<dt class="col-xl-6">{% trans 'section'|capfirst %}</dt>
<dd class="col-xl-6">{{ user_object.profile.section }}</dd>
<dt class="col-xl-6">{% trans 'email'|capfirst %}</dt>
<dd class="col-xl-6"><a href="mailto:{{ user_object.email }}">{{ user_object.email }}</a></dd>
<dt class="col-xl-6">{% trans 'email'|capfirst %}</dt>
<dd class="col-xl-6"><a href="mailto:{{ user_object.email }}">{{ user_object.email }}</a></dd>
<dt class="col-xl-6">{% trans 'phone number'|capfirst %}</dt>
<dd class="col-xl-6"><a href="tel:{{ user_object.profile.phone_number }}">{{ user_object.profile.phone_number }}</a>
</dd>
<dt class="col-xl-6">{% trans 'phone number'|capfirst %}</dt>
<dd class="col-xl-6"><a href="tel:{{ user_object.profile.phone_number }}">{{ user_object.profile.phone_number }}</a>
</dd>
<dt class="col-xl-6">{% trans 'address'|capfirst %}</dt>
<dd class="col-xl-6">{{ user_object.profile.address }}</dd>
<dt class="col-xl-6">{% trans 'address'|capfirst %}</dt>
<dd class="col-xl-6">{{ user_object.profile.address }}</dd>
{% if user_object.note and "note.view_note"|has_perm:user_object.note %}
<dt class="col-xl-6">{% trans 'balance'|capfirst %}</dt>
<dd class="col-xl-6">{{ user_object.note.balance | pretty_money }}</dd>
{% if user_object.note and "note.view_note"|has_perm:user_object.note %}
<dt class="col-xl-6">{% trans 'balance'|capfirst %}</dt>
<dd class="col-xl-6">{{ user_object.note.balance | pretty_money }}</dd>
<dt class="col-xl-6">{% trans 'paid'|capfirst %}</dt>
<dd class="col-xl-6">{{ user_object.profile.paid|yesno }}</dd>
{% endif %}
<dt class="col-xl-6">{% trans 'paid'|capfirst %}</dt>
<dd class="col-xl-6">{{ user_object.profile.paid|yesno }}</dd>
{% endif %}
</dl>

View File

@ -5,7 +5,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% load i18n perms %}
{% block content %}
{% if can_manage_registrations %}
{% if "member.change_profile_registration_valid"|has_perm:user %}
<a class="btn btn-block btn-secondary mb-3" href="{% url 'registration:future_user_list' %}">
<i class="fa fa-user-plus"></i> {% trans "Registrations" %}
</a>

View File

@ -70,11 +70,10 @@ class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
form.fields['email'].required = True
form.fields['email'].help_text = _("This address must be valid.")
if PermissionBackend.check_perm(self.request.user, "member.change_profile", context['user_object'].profile):
context['profile_form'] = self.profile_form(instance=context['user_object'].profile,
data=self.request.POST if self.request.POST else None)
if not self.object.profile.report_frequency:
del context['profile_form'].fields["last_report"]
context['profile_form'] = self.profile_form(instance=context['user_object'].profile,
data=self.request.POST if self.request.POST else None)
if not self.object.profile.report_frequency:
del context['profile_form'].fields["last_report"]
return context
@ -235,13 +234,6 @@ class UserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
return qs
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
pre_registered_users = User.objects.filter(PermissionBackend.filter_queryset(self.request.user, User, "view"))\
.filter(profile__registration_valid=False)
context["can_manage_registrations"] = pre_registered_users.exists()
return context
class ProfileAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
"""
@ -255,8 +247,8 @@ class ProfileAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
note = context['object'].note
context["aliases"] = AliasTable(
note.alias_set.filter(PermissionBackend.filter_queryset(self.request.user, Alias, "view")).distinct().all())
context["aliases"] = AliasTable(note.alias_set.filter(PermissionBackend
.filter_queryset(self.request.user, Alias, "view")).all())
context["can_create"] = PermissionBackend.check_perm(self.request.user, "note.add_alias", Alias(
note=context["object"].note,
name="",
@ -458,8 +450,8 @@ class ClubAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
note = context['object'].note
context["aliases"] = AliasTable(note.alias_set.filter(
PermissionBackend.filter_queryset(self.request.user, Alias, "view")).distinct().all())
context["aliases"] = AliasTable(note.alias_set.filter(PermissionBackend
.filter_queryset(self.request.user, Alias, "view")).all())
context["can_create"] = PermissionBackend.check_perm(self.request.user, "note.add_alias", Alias(
note=context["object"].note,
name="",
@ -678,13 +670,11 @@ class ClubAddMemberView(ProtectQuerysetMixin, ProtectedCreateView):
if not last_name or not first_name or (not bank and credit_type.special_type == "Chèque"):
if not last_name:
form.add_error('last_name', _("This field is required."))
error = True
if not first_name:
form.add_error('first_name', _("This field is required."))
error = True
if not bank and credit_type.special_type == "Chèque":
form.add_error('bank', _("This field is required."))
error = True
return self.form_invalid(form)
return not error

View File

@ -799,12 +799,12 @@
"member",
"membership"
],
"query": "{}",
"query": "{\"club\": [\"club\"]}",
"type": "change",
"mask": 3,
"field": "roles",
"permanent": false,
"description": "Modifier les rôles d'une adhésion"
"description": "Modifier les rôles d'un adhérent d'un club"
}
},
{
@ -2081,7 +2081,7 @@
],
"query": "{}",
"type": "change",
"mask": 2,
"mask": 1,
"field": "invalidity_reason",
"permanent": false,
"description": "Modifier la raison d'invalidité d'une transaction"
@ -2807,70 +2807,6 @@
"description": "Voir ses propres alias, pour toujours"
}
},
{
"model": "permission.permission",
"pk": 180,
"fields": {
"model": [
"auth",
"user"
],
"query": "{\"profile__registration_valid\": false}",
"type": "view",
"mask": 2,
"field": "",
"permanent": false,
"description": "Voir n'importe quel utilisateur non encore inscrit"
}
},
{
"model": "permission.permission",
"pk": 181,
"fields": {
"model": [
"member",
"profile"
],
"query": "{\"registration_valid\": false}",
"type": "view",
"mask": 2,
"field": "",
"permanent": false,
"description": "Voir n'importe quel profil non encore inscrit"
}
},
{
"model": "permission.permission",
"pk": 182,
"fields": {
"model": [
"auth",
"user"
],
"query": "{\"memberships__club__name\": \"BDE\", \"memberships__roles__name\": \"Adhérent BDE\", \"memberships__date_start__lte\": [\"today\"], \"memberships__date_end__gte\": [\"today\"]}",
"type": "view",
"mask": 2,
"field": "",
"permanent": false,
"description": "Voir n'importe quel utilisateur qui est adhérent BDE"
}
},
{
"model": "permission.permission",
"pk": 183,
"fields": {
"model": [
"note",
"note"
],
"query": "{}",
"type": "change",
"mask": 1,
"field": "display_image",
"permanent": false,
"description": "Changer l'image de n'importe quelle note"
}
},
{
"model": "permission.role",
"pk": 1,
@ -3003,14 +2939,14 @@
62,
127,
133,
135,
136,
141,
142,
150,
166,
167,
168,
182
168
]
}
},
@ -3086,8 +3022,7 @@
175,
176,
177,
178,
183
178
]
}
},
@ -3270,12 +3205,7 @@
175,
176,
177,
178,
179,
180,
181,
182,
183
178
]
}
},
@ -3309,12 +3239,7 @@
170,
171,
176,
177,
178,
179,
180,
181,
182
177
]
}
},
@ -3477,6 +3402,7 @@
135,
136,
137,
138,
139,
140,
143,
@ -3489,41 +3415,6 @@
]
}
},
{
"model": "permission.role",
"pk": 20,
"fields": {
"for_club": 2,
"name": "PC Kfet",
"permissions": [
6,
22,
24,
25,
26,
27,
30,
49,
50,
55,
56,
57,
58,
137,
143,
147,
150,
166,
167,
168,
176,
177,
180,
181,
182
]
}
},
{
"model": "wei.weirole",
"pk": 12,

View File

@ -45,7 +45,6 @@ class InstancedPermission:
with transaction.atomic():
sid = transaction.savepoint()
for o in self.model.model_class().objects.filter(pk=0).all():
o._no_signal = True
o._force_delete = True
Model.delete(o)
# An object with pk 0 wouldn't deleted. That's not normal, we alert admins.
@ -63,6 +62,10 @@ class InstancedPermission:
obj._no_signal = True
Model.save(obj, force_insert=True)
ret = self.model.model_class().objects.filter(self.query & Q(pk=0)).exists()
# Delete testing object
obj._no_signal = True
obj._force_delete = True
Model.delete(obj)
transaction.savepoint_rollback(sid)
return ret

View File

@ -51,10 +51,8 @@ class ProtectQuerysetMixin:
# No worry if the user change the hidden fields: a 403 error will be performed if the user tries to make
# a custom request.
# We could also delete the field, but some views might be affected.
meta = form.instance._meta
for key in form.base_fields:
if not PermissionBackend.check_perm(self.request.user,
f"{meta.app_label}.change_{meta.model_name}_" + key, self.object):
if not PermissionBackend.check_perm(self.request.user, "wei.change_weiregistration_" + key, self.object):
form.fields[key].widget = HiddenInput()
return form

View File

@ -4,8 +4,6 @@
import django_tables2 as tables
from django.contrib.auth.models import User
from treasury.models import SogeCredit
class FutureUserTable(tables.Table):
"""
@ -23,7 +21,6 @@ class FutureUserTable(tables.Table):
fields = ('last_name', 'first_name', 'username', 'email', )
model = User
row_attrs = {
'class': lambda record: 'table-row'
+ (' bg-warning' if SogeCredit.objects.filter(user=record).exists() else ''),
'class': 'table-row',
'data-href': lambda record: record.pk
}

View File

@ -235,7 +235,7 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
fee += 8000
ctx["total_fee"] = "{:.02f}".format(fee / 100, )
ctx["declare_soge_account"] = SogeCredit.objects.filter(user=user).exists()
ctx["declare_soge_account"] = True
return ctx

View File

@ -28,8 +28,6 @@ class TreasuryConfig(AppConfig):
source__in=NoteSpecial.objects.filter(~Q(remittancetype=None)),
specialtransactionproxy=None,
):
proxy = SpecialTransactionProxy(transaction=transaction, remittance=None)
proxy._force_save = True
proxy.save()
SpecialTransactionProxy.objects.create(transaction=transaction, remittance=None)
post_migrate.connect(setup_specialtransactions_proxies, sender=SpecialTransactionProxy)

View File

@ -10,8 +10,9 @@ def save_special_transaction(instance, created, **kwargs):
"""
if not hasattr(instance, "_no_signal"):
if created and RemittanceType.objects.filter(
note=instance.source if instance.is_credit() else instance.destination).exists():
proxy = SpecialTransactionProxy(transaction=instance, remittance=None)
proxy._force_save = True
proxy.save()
if instance.is_credit():
if created and RemittanceType.objects.filter(note=instance.source).exists():
SpecialTransactionProxy.objects.create(transaction=instance, remittance=None).save()
else:
if created and RemittanceType.objects.filter(note=instance.destination).exists():
SpecialTransactionProxy.objects.create(transaction=instance, remittance=None).save()

View File

@ -147,4 +147,4 @@ class SogeCreditTable(tables.Table):
class Meta:
model = SogeCredit
fields = ('user', 'user__last_name', 'user__first_name', 'amount', 'valid', )
fields = ('user', 'amount', 'valid', )

View File

@ -11,14 +11,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
</div>
<div class="card-body">
<dl class="row">
<dt class="col-xl-6 text-right">{% trans 'last name'|capfirst %}</dt>
<dd class="col-xl-6">{{ object.user.last_name }}</dd>
<dt class="col-xl-6 text-right">{% trans 'first name'|capfirst %}</dt>
<dd class="col-xl-6">{{ object.user.first_name }}</dd>
<dt class="col-xl-6 text-right">{% trans 'username'|capfirst %}</dt>
<dd class="col-xl-6"><a href="{% url 'member:user_detail' pk=object.user.pk %}">{{ object.user.username }}</a></dd>
<dt class="col-xl-6 text-right">{% trans 'user'|capfirst %}</dt>
<dd class="col-xl-6"><a href="{% url 'member:user_detail' pk=object.user.pk %}">{{ object.user }}</a></dd>
{% if "note.view_note_balance"|has_perm:object.user.note %}
<dt class="col-xl-6 text-right">{% trans 'balance'|capfirst %}</dt>

View File

@ -2,12 +2,12 @@
# SPDX-License-Identifier: GPL-3.0-or-later
from django.conf import settings
from django.contrib.auth import login
from django.contrib.auth.models import AnonymousUser, User
from django.contrib.sessions.backends.db import SessionStore
from threading import local
from django.contrib.sessions.backends.db import SessionStore
USER_ATTR_NAME = getattr(settings, 'LOCAL_USER_ATTR_NAME', '_current_user')
SESSION_ATTR_NAME = getattr(settings, 'LOCAL_SESSION_ATTR_NAME', '_current_session')
IP_ATTR_NAME = getattr(settings, 'LOCAL_IP_ATTR_NAME', '_current_ip')
@ -78,41 +78,6 @@ class SessionMiddleware(object):
return response
class LoginByIPMiddleware(object):
"""
Allow some users to be authenticated based on their IP address.
For example, the "note" account should not be used elsewhere than the Kfet computer,
and should not have any password.
The password that is stored in database should be on the form "ipbased$my.public.ip.address".
"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
"""
If the user is not authenticated, get the used IP address
and check if an user is authorized to be automatically logged with this address.
If it is the case, the logging is performed with the full rights.
"""
if not request.user.is_authenticated:
if 'HTTP_X_REAL_IP' in request.META:
ip = request.META.get('HTTP_X_REAL_IP')
elif 'HTTP_X_FORWARDED_FOR' in request.META:
ip = request.META.get('HTTP_X_FORWARDED_FOR').split(', ')[0]
else:
ip = request.META.get('REMOTE_ADDR')
qs = User.objects.filter(password=f"ipbased${ip}")
if qs.exists():
login(request, qs.get())
session = request.session
session["permission_mask"] = 42
session.save()
return self.get_response(request)
class TurbolinksMiddleware(object):
"""
Send the `Turbolinks-Location` header in response to a visit that was redirected,

View File

@ -49,6 +49,9 @@ try:
except ImportError:
pass
if "logs" in INSTALLED_APPS:
MIDDLEWARE += ('note_kfet.middlewares.SessionMiddleware',)
if DEBUG:
PASSWORD_HASHERS += ['member.hashers.DebugSuperuserBackdoor']
if "debug_toolbar" in INSTALLED_APPS:

View File

@ -79,8 +79,6 @@ MIDDLEWARE = [
'django.middleware.locale.LocaleMiddleware',
'django.contrib.sites.middleware.CurrentSiteMiddleware',
'django_htcpcp_tea.middleware.HTCPCPTeaMiddleware',
'note_kfet.middlewares.SessionMiddleware',
'note_kfet.middlewares.LoginByIPMiddleware',
'note_kfet.middlewares.TurbolinksMiddleware',
]

View File

@ -7,7 +7,7 @@ django-extensions~=2.1.4
django-filter~=2.1.0
django-htcpcp-tea~=0.3.1
django-mailer~=2.0.1
django-oauth-toolkit~=1.3.3
django-oauth-toolkit~=1.1.2
django-phonenumber-field~=5.0.0
django-polymorphic~=2.0.3
djangorestframework~=3.9.0