1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2025-06-22 18:38:23 +02:00

Compare commits

..

2 Commits

Author SHA1 Message Date
17be896a99 [permission] Add PermissionVar model 2022-10-10 19:37:51 +02:00
a69573ccdb Fix permission that allows users to create OAuth2 apps
Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
2022-08-29 11:21:45 +02:00
17 changed files with 423 additions and 221 deletions

View File

@ -6,7 +6,7 @@
"name": "Pot", "name": "Pot",
"manage_entries": true, "manage_entries": true,
"can_invite": true, "can_invite": true,
"guest_entry_fee": 1000 "guest_entry_fee": 500
} }
}, },
{ {
@ -28,25 +28,5 @@
"can_invite": false, "can_invite": false,
"guest_entry_fee": 0 "guest_entry_fee": 0
} }
},
{
"model": "activity.activitytype",
"pk": 5,
"fields": {
"name": "Soir\u00e9e avec entrées",
"manage_entries": true,
"can_invite": false,
"guest_entry_fee": 0
}
},
{
"model": "activity.activitytype",
"pk": 7,
"fields": {
"name": "Soir\u00e9e avec invitations",
"manage_entries": true,
"can_invite": true,
"guest_entry_fee": 0
}
} }
] ]

View File

@ -1,5 +0,0 @@
from rest_framework.pagination import PageNumberPagination
class CustomPagination(PageNumberPagination):
page_size_query_param = 'page_size'

View File

@ -221,7 +221,7 @@ function consume (source, source_alias, dest, quantity, amount, reason, type, ca
.done(function () { .done(function () {
if (!isNaN(source.balance)) { if (!isNaN(source.balance)) {
const newBalance = source.balance - quantity * amount const newBalance = source.balance - quantity * amount
if (newBalance <= -2000) { if (newBalance <= -5000) {
addMsg(interpolate(gettext('Warning, the transaction from the note %s succeed, ' + addMsg(interpolate(gettext('Warning, the transaction from the note %s succeed, ' +
'but the emitter note %s is very negative.'), [source_alias, source_alias]), 'danger', 30000) 'but the emitter note %s is very negative.'), [source_alias, source_alias]), 'danger', 30000)
} else if (newBalance < 0) { } else if (newBalance < 0) {

View File

@ -314,7 +314,7 @@ $('#btn_transfer').click(function () {
if (!isNaN(source.note.balance)) { if (!isNaN(source.note.balance)) {
const newBalance = source.note.balance - source.quantity * dest.quantity * amount const newBalance = source.note.balance - source.quantity * dest.quantity * amount
if (newBalance <= -2000) { if (newBalance <= -5000) {
addMsg(interpolate(gettext('Warning, the transaction of %s from the note %s to the note %s succeed, but the emitter note %s is very negative.'), addMsg(interpolate(gettext('Warning, the transaction of %s from the note %s to the note %s succeed, but the emitter note %s is very negative.'),
[pretty_money(source.quantity * dest.quantity * amount), source.name, dest.name, source.name]), 'danger', 10000) [pretty_money(source.quantity * dest.quantity * amount), source.name, dest.name, source.name]), 'danger', 10000)
reset() reset()

View File

@ -4,7 +4,7 @@
from django.contrib import admin from django.contrib import admin
from note_kfet.admin import admin_site from note_kfet.admin import admin_site
from .models import Permission, PermissionMask, Role from .models import Permission, PermissionVar, PermissionMask, Role
@admin.register(PermissionMask, site=admin_site) @admin.register(PermissionMask, site=admin_site)
@ -15,6 +15,14 @@ class PermissionMaskAdmin(admin.ModelAdmin):
list_display = ('description', 'rank', ) list_display = ('description', 'rank', )
@admin.register(PermissionVar, site=admin_site)
class PermissionVarAdmin(admin.ModelAdmin):
"""
Admin customisation for PermissionVar
"""
list_display = ('name', 'description',)
@admin.register(Permission, site=admin_site) @admin.register(Permission, site=admin_site)
class PermissionAdmin(admin.ModelAdmin): class PermissionAdmin(admin.ModelAdmin):
""" """

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +0,0 @@
# Generated by Django 2.2.28 on 2023-07-24 10:15
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('permission', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='role',
name='for_club',
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.PROTECT, to='member.Club', verbose_name='for club'),
),
]

View File

@ -0,0 +1,22 @@
# Generated by Django 2.2.28 on 2022-10-10 17:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('permission', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='PermissionVar',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.SlugField(unique=True, verbose_name='name')),
('query', models.TextField(verbose_name='query')),
('description', models.CharField(blank=True, max_length=255, verbose_name='description')),
],
),
]

View File

@ -118,6 +118,25 @@ class PermissionMask(models.Model):
verbose_name_plural = _("permission masks") verbose_name_plural = _("permission masks")
class PermissionVar(models.Model):
name = models.SlugField(
unique=True,
blank=False,
verbose_name=_("name"),
)
query = models.TextField(
verbose_name=_("query"),
)
description = models.CharField(
max_length=255,
blank=True,
verbose_name=_("description"),
)
class Permission(models.Model): class Permission(models.Model):
PERMISSION_TYPES = [ PERMISSION_TYPES = [
@ -139,6 +158,7 @@ class Permission(models.Model):
# query -> ["AND", query, …] AND multiple queries # query -> ["AND", query, …] AND multiple queries
# | ["OR", query, …] OR multiple queries # | ["OR", query, …] OR multiple queries
# | ["NOT", query] Opposite of query # | ["NOT", query] Opposite of query
# | ["VAR", query] A var name as defined in PermissionVar
# query -> {key: value, …} A list of fields and values of a Q object # query -> {key: value, …} A list of fields and values of a Q object
# key -> string A field name # key -> string A field name
# value -> int | string | bool | null Literal values # value -> int | string | bool | null Literal values
@ -150,6 +170,7 @@ class Permission(models.Model):
# | ["MUL", oper, …] Multiply F objects or literals # | ["MUL", oper, …] Multiply F objects or literals
# | int | string | bool | null Literal values # | int | string | bool | null Literal values
# | ["F", string] A field # | ["F", string] A field
# | ["VAR", string] A var name as defined in PermissionVar
# #
# Examples: # Examples:
# Q(is_superuser=True) := {"is_superuser": true} # Q(is_superuser=True) := {"is_superuser": true}
@ -215,6 +236,8 @@ class Permission(models.Model):
return functools.reduce(operator.mul, [Permission.compute_f(oper, **kwargs) for oper in oper[1:]]) return functools.reduce(operator.mul, [Permission.compute_f(oper, **kwargs) for oper in oper[1:]])
elif oper[0] == 'F': elif oper[0] == 'F':
return F(oper[1]) return F(oper[1])
elif oper[0] == 'VAR':
return compute_f(json.loads(PermissionVar.objects.get(name=oper[1]).query), **kwargs)
else: else:
field = kwargs[oper[0]] field = kwargs[oper[0]]
for i in range(1, len(oper)): for i in range(1, len(oper)):
@ -289,6 +312,8 @@ class Permission(models.Model):
return functools.reduce(operator.or_, [Permission._about(query, **kwargs) for query in query[1:]]) return functools.reduce(operator.or_, [Permission._about(query, **kwargs) for query in query[1:]])
elif query[0] == 'NOT': elif query[0] == 'NOT':
return ~Permission._about(query[1], **kwargs) return ~Permission._about(query[1], **kwargs)
elif query[0] == 'VAR':
return Permission._about(json.loads(PermissionVar.objects.get(name=query[1]).query), **kwargs)
else: else:
return Q(pk=F("pk")) if Permission.compute_param(query, **kwargs) else ~Q(pk=F("pk")) return Q(pk=F("pk")) if Permission.compute_param(query, **kwargs) else ~Q(pk=F("pk"))
elif isinstance(query, dict): elif isinstance(query, dict):
@ -339,7 +364,6 @@ class Role(models.Model):
"member.Club", "member.Club",
verbose_name=_("for club"), verbose_name=_("for club"),
on_delete=models.PROTECT, on_delete=models.PROTECT,
blank=True,
null=True, null=True,
default=None, default=None,
) )

View File

@ -310,8 +310,8 @@ class SogeCredit(models.Model):
amount = sum(transaction.total for transaction in self.transactions.all()) amount = sum(transaction.total for transaction in self.transactions.all())
if 'wei' in settings.INSTALLED_APPS: if 'wei' in settings.INSTALLED_APPS:
from wei.models import WEIMembership from wei.models import WEIMembership
if not WEIMembership.objects\ if not WEIMembership.objects.filter(club__weiclub__year=datetime.date.today().year, user=self.user)\
.filter(club__weiclub__year=self.credit_transaction.created_at.year, user=self.user).exists(): .exists():
# 80 € for people that don't go to WEI # 80 € for people that don't go to WEI
amount += 8000 amount += 8000
return amount return amount
@ -329,18 +329,17 @@ class SogeCredit(models.Model):
bde_qs = Membership.objects.filter(user=self.user, club=bde, date_start__gte=bde.membership_start) bde_qs = Membership.objects.filter(user=self.user, club=bde, date_start__gte=bde.membership_start)
kfet_qs = Membership.objects.filter(user=self.user, club=kfet, date_start__gte=kfet.membership_start) kfet_qs = Membership.objects.filter(user=self.user, club=kfet, date_start__gte=kfet.membership_start)
## Soge do not pay BDE and kfet memberships this year (2022-2023) if bde_qs.exists():
# if bde_qs.exists(): m = bde_qs.get()
# m = bde_qs.get() if MembershipTransaction.objects.filter(membership=m).exists(): # non-free membership
# if MembershipTransaction.objects.filter(membership=m).exists(): # non-free membership if m.transaction not in self.transactions.all():
# if m.transaction not in self.transactions.all(): self.transactions.add(m.transaction)
# self.transactions.add(m.transaction)
# if kfet_qs.exists():
# if kfet_qs.exists(): m = kfet_qs.get()
# m = kfet_qs.get() if MembershipTransaction.objects.filter(membership=m).exists(): # non-free membership
# if MembershipTransaction.objects.filter(membership=m).exists(): # non-free membership if m.transaction not in self.transactions.all():
# if m.transaction not in self.transactions.all(): self.transactions.add(m.transaction)
# self.transactions.add(m.transaction)
if 'wei' in settings.INSTALLED_APPS: if 'wei' in settings.INSTALLED_APPS:
from wei.models import WEIClub from wei.models import WEIClub

View File

@ -108,7 +108,7 @@ class InvoiceListView(LoginRequiredMixin, SingleTableView):
name="", name="",
address="", address="",
) )
if not PermissionBackend.check_perm(self.request, "treasury.view_invoice", sample_invoice): if not PermissionBackend.check_perm(self.request, "treasury.add_invoice", sample_invoice):
raise PermissionDenied(_("You are not able to see the treasury interface.")) raise PermissionDenied(_("You are not able to see the treasury interface."))
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)

View File

@ -14,17 +14,14 @@ from .base import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm, WEIBusInf
from ...models import WEIMembership from ...models import WEIMembership
WORDS = [ WORDS = [
'ABBA', 'After', 'Alcoolique anonyme', 'Ambiance festive', 'Années 2000', 'Apéro', 'Art', '13 organisé', '3ième mi temps', 'Années 2000', 'Apéro', 'BBQ', 'BP', 'Beauf', 'Binge drinking', 'Bon enfant',
'Baby foot billard biere pong', 'BBQ', 'Before', 'Bière pong', 'Bon enfant', 'Calme', 'Canapé', 'Cartouche', 'Catacombes', 'Chansons paillardes', 'Chansons populaires', 'Chanteur', 'Chartreuse', 'Chill',
'Chanson paillarde', 'Chanson populaire', 'Chartreuse', 'Cheerleader', 'Chill', 'Choré', 'Core', 'DJ', 'Dancefloor', 'Danse', 'David Guetta', 'Disco', 'Eau de vie', 'Électro', 'Escalade', 'Familial',
'Cinéma', 'Cocktail', 'Comédie musicle', 'Commercial', 'Copaing', 'Danse', 'Dancefloor', 'Fanfare', 'Fracassage', 'Féria', 'Hard rock', 'Hoeggarden', 'House', 'Huit-six', 'IPA', 'Inclusif', 'Inferno',
'Electro', 'Fanfare', 'Gin tonic', 'Inclusif', 'Jazz', "Jeux d'alcool", 'Jeux de carte', 'Introverti', 'Jager bomb', 'Jazz', 'Jeux d\'alcool', 'Jeux de rôles', 'Jeux vidéo', 'Jul', 'Jus de fruit',
'Jeux de rôle', 'Jeux de société', 'JUL', 'Jus de fruit', 'Kfet', 'Kleptomanie assurée', 'Karaoké', 'LGBTQI+', 'Lady Gaga', 'Loup garou', 'Morning beer', 'Métal', 'Nuit blanche', 'Ovalie', 'Psychedelic',
'LGBTQ+', 'Livre', 'Morning beer', 'Musique', 'NAPS', 'Paillettes', 'Pastis', 'Paté Hénaff', 'Pétanque', 'Rave', 'Reggae', 'Rhum', 'Ricard', 'Rock', 'Rosé', 'Rétro', 'Séducteur', 'Techno', 'Thérapie taxi',
'Peluche', 'Pena baiona', "Peu d'alcool", 'Pilier de bar', 'PMU', 'Poulpe', 'Punch', 'Rap', 'Théâtre', 'Trap', 'Turn up', 'Underground', 'Volley', 'Wati B', 'Zinédine Zidane',
'Réveil', 'Rock', 'Rugby', 'Sandwich', 'Serge', 'Shot', 'Sociable', 'Spectacle', 'Techno',
'Techno house', 'Thérapie Taxi', 'Tradition kchanaises', 'Troisième mi-temps', 'Turn up',
'Vodka', 'Vodka pomme', 'Volley', 'Vomi stratégique'
] ]

View File

@ -118,13 +118,13 @@ Exemples
{"F": [ {"F": [
"ADD", "ADD",
["F", "source__balance"], ["F", "source__balance"],
2000] 5000]
} }
} }
] ]
| si la destination est la note du club dont on est membre et si le montant est inférieur au solde de la source + 20 €, | si la destination est la note du club dont on est membre et si le montant est inférieur au solde de la source + 50 €,
autrement dit le solde final est au-dessus de -20 €. autrement dit le solde final est au-dessus de -50 €.
Masques de permissions Masques de permissions

View File

@ -83,6 +83,13 @@ Je suis trésorier d'un club, qu'ai-je le droit de faire ?
bien sûr permis pour faciliter des transferts. Tout abus de droits constaté bien sûr permis pour faciliter des transferts. Tout abus de droits constaté
pourra mener à des sanctions prises par le bureau du BDE. pourra mener à des sanctions prises par le bureau du BDE.
.. warning::
Une fonctionnalité pour permettre de gérer plus proprement les remboursements
entre amis est en cours de développement. Temporairement et pour des raisons
de confort, les trésoriers de clubs ont le droit de prélever n'importe quelle
adhérente vers n'importe quelle autre note adhérente, tant que la source ne
descend pas sous ``- 50 €``. Ces droits seront retirés d'ici quelques semaines.
Je suis trésorier d'un club, je n'arrive pas à voir le solde du club / faire des transactions Je suis trésorier d'un club, je n'arrive pas à voir le solde du club / faire des transactions
--------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------

View File

@ -252,7 +252,7 @@ REST_FRAMEWORK = {
'rest_framework.authentication.TokenAuthentication', 'rest_framework.authentication.TokenAuthentication',
'oauth2_provider.contrib.rest_framework.OAuth2Authentication', 'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
], ],
'DEFAULT_PAGINATION_CLASS': 'apps.api.pagination.CustomPagination', 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 20, 'PAGE_SIZE': 20,
} }

View File

@ -96,7 +96,7 @@ function displayStyle (note) {
if (!note) { return '' } if (!note) { return '' }
const balance = note.balance const balance = note.balance
var css = '' var css = ''
if (balance < -2000) { css += ' text-danger bg-dark' } if (balance < -5000) { css += ' text-danger bg-dark' }
else if (balance < -1000) { css += ' text-danger' } else if (balance < -1000) { css += ' text-danger' }
else if (balance < 0) { css += ' text-warning' } else if (balance < 0) { css += ' text-warning' }
if (!note.email_confirmed) { css += ' bg-primary' } if (!note.email_confirmed) { css += ' bg-primary' }

View File

@ -23,11 +23,11 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% csrf_token %} {% csrf_token %}
{{ form|crispy }} {{ form|crispy }}
{{ profile_form|crispy }} {{ profile_form|crispy }}
{% comment "Soge not for membership (only WEI)" %} {{ soge_form|crispy }} {% endcomment %} {{ soge_form|crispy }}
<button class="btn btn-success" type="submit"> <button class="btn btn-success" type="submit">
{% trans "Sign up" %} {% trans "Sign up" %}
</button> </button>
</form> </form>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}